npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-localstorage-ts

v2.3.2

Published

A small library to wrap browser's localstorage in a functional fashion.

Downloads

1,366

Readme

release semantic-release Conventional Commits

react-localstorage-ts

A small layer over the browser's localstorage, fallbacks to an in-memory store if localstorage is not supported by the browser.

Built with fp-ts in mind, react-localstorage-ts gives you a standard way to access objects stored locally.

install

yarn

yarn add react-localstorage-ts

npm

npm install -S react-localstorage-ts

quick start

First create the hooks to read/write the values you just defined:

// localHooks.ts
import * as t from "io-ts"
import { makeHooksFromStorage, createLocalStorage } from "react-localstorage-ts"
import { ThemeFlavourCodec, AuthTokenCodec } from "./codecs"

export const localStorage = createLocalStorage(
  {
    themeFlavour: ThemeFlavourCodec,
    authToken: AuthTokenCodec,
  },
  { defaultValues: { themeFlavour: "light" } },
)

export const hooks = makeHooksFromStorage(storage)

then you use them in your react components:

// App.tsx
import * as React from "react"
import * as LV from "react-localstorage-ts/LocalValue"
import LightThemeApp from "./components/LightThemeApp"
import DarkThemeApp from "./components/DarkThemeApp"
import { useThemeFlavour } from "./localHooks"

const App: React.FC = () => {
  const [themeFlavour, setThemeFlavour] = hooks.useThemeFlavour()

  return pipe(
    theme,
    LV.fold2(
      () => {
        console.error("wrong value stored in localStorage!")
      },
      (themeFlavour) => {
        switch (themeFlavour) {
          case "light": {
            return <LightThemeApp />
          }
          case "dark": {
            return <DarkThemeApp />
          }
        }
      },
    ),
  )
}

export default App

LocalValue

A new data structure is defined for items stored in localstorage, LocalValue. When dealing with a value stored in your localstorage there are three possibilities:

  1. there is no value in your localstorage (optionality).
  2. the value is present, but it is wrong (correctness).
  3. the value is present and it is valid (also correctness).

LocalValue introduces a sum type that represents the optionality/correctness dicotomy:

export interface Absent {
  readonly _tag: "Absent"
}

export interface Invalid<E> {
  readonly _tag: "Invalid"
  readonly errors: E
}
export interface Valid<A> {
  readonly _tag: "Valid"
  readonly value: A
}

export type LocalValue<E, A> = Absent | Invalid<E> | Valid<A>

It also has instances for some of the most common fp-ts type-classes, so that you can use it in the same way you usually use other fp-ts abstractions:

// LoginLayout.tsx
import * as LV from "react-localstorage-ts/LocalValue"
import { useAccessToken } from "./localHooks"
import { goToLoginPage } from "./router"
import App from "./App"

const LoginLayout: React.FC = ({ children }) => {
  const [token] = useAccessToken()

  React.useEffect(() => {
    if (!LV.isValid) {
      goToLoginPage()
    }
  }, [])

  return pipe(
    token,
    LV.fold(
      () => "no token in storage",
      () => "malformed token in storage",
      () => <>{children}</>,
    ), // N.B. when you want to treat the "absent" and "incorrect" case in the same way, you can use fold2 and only define two handling funcitons
  )
}

// LoginPage.tsx
import * as LV from "react-localstorage-ts/LocalValue"
import { goToHomePage } from "./router"
import { useAccessToken } from "./localHooks"

const LoginPage: React.FC = ({ children }) => {
  const [token, setToken] = useAccessToken()

  React.useEffect(() => {
    if (LV.isValid(token)) {
      goToHomePage()
    }
  }, [])

  return (
    <Form
      onSubmit={(formValues) =>
        api.getToken(formValues).then((t) => setToken(t))
      }
    />
  )
}

defining codecs

Given that browsers only allows you to store serialized data in string format, codecs must conform to the shape Codec<E, string, B>, where E is the type of the decoding error, string is the type of the data before decoding and B is the type of the runtime value.

If you use io-ts you can simply create a layer to convert io-ts codecs to Codec compliant instances:

import { pipe } from "fp-ts/lib/function"
import * as t from "io-ts"
import * as E from "fp-ts/Either"
import { Json, JsonFromString } from "io-ts-types"
import * as LV from "react-localstorage-ts/LocalValue"
import { Codec } from "react-localstorage-ts/Codec"

const adaptIoTsCodec = <A, B>(C: t.Type<B, A>): Codec<t.Errors, A, B> => {
  return {
    encode: C.encode,
    decode: (u: unknown) => LV.fromEither(C.decode(u)),
  }
}

export const fromIoTsCodec = <A, B extends Json>(C: t.Type<A, B>) => {
  const stringCodec = new t.Type<A, string>(
    C.name,
    C.is,
    (u, c) => {
      return pipe(
        t.string.validate(u, c),
        E.chain((jsonString) => JsonFromString.validate(jsonString, c)),
        E.chain((json) => C.validate(json, c)),
      )
    },
    (v) => {
      return pipe(v, C.encode, JsonFromString.encode)
    },
  )

  return adaptIoTsCodec(stringCodec)
}

If you want to update your localstorage from outside of a react component while still having your components "react" to the change, you can use the utilities getLocalValue, setLocalElement and removeLocalElement.

contributing

to commit to this repository there are a few rules:

  • your commits must follow the conventional commit standard (it should be enforced by husky commit-msg hook).
  • your code must be formatted using prettier.
  • all tests must pass.

release flow

here you can find an explanation of the release flow.