react-localstorage-ts
Advanced tools
Comparing version 2.0.0-beta.14 to 2.0.0-beta.15
{ | ||
"name": "react-localstorage-ts", | ||
"version": "2.0.0-beta.14", | ||
"version": "2.0.0-beta.15", | ||
"description": "A small library to wrap browser's localstorage in a functional fashion.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
106
README.md
@@ -14,6 +14,9 @@ ![release](https://github.com/fido-id/react-localstorage-ts/actions/workflows/release.yml/badge.svg) | ||
### yarn | ||
```shell | ||
yarn add react-localstorage-ts | ||
``` | ||
### npm | ||
```shell | ||
@@ -24,2 +27,3 @@ npm install -S react-localstorage-ts | ||
## quick start | ||
First create the hooks to read/write the values you just defined: | ||
@@ -30,20 +34,22 @@ | ||
import * as t from "io-ts" | ||
import { | ||
makeUseLocalItem, | ||
} from "react-localstorage-ts" | ||
import {ThemeFlavour} from "./codecs" | ||
import { makeHooksFromStorage, createLocalStorage } from "react-localstorage-ts" | ||
import { ThemeFlavourCodec, AuthTokenCodec } from "./codecs" | ||
export const useThemeFlavour = makeUseLocalItem( | ||
"theme", | ||
ThemeFlavour, | ||
{ defaultValue: "light" }, | ||
export const localStorage = createLocalStorage( | ||
{ | ||
themeFlavour: ThemeFlavourCodec, | ||
authToken: AuthTokenCodec, | ||
}, | ||
{ defaultValues: { themeFlavour: "light" } }, | ||
) | ||
export const useAccessToken = makeUseLocalItem("access_token", t.string) | ||
export const hooks = makeHooksFromStorage(storage) | ||
``` | ||
then you use them in your react components: | ||
```tsx | ||
// App.tsx | ||
import * as React from "react" | ||
import * as E from "fp-ts/Either" | ||
import * as LV from "react-localstorage-ts/LocalValue" | ||
import LightThemeApp from "./components/LightThemeApp" | ||
@@ -54,11 +60,11 @@ import DarkThemeApp from "./components/DarkThemeApp" | ||
const App: React.FC = () => { | ||
const [theme, setTheme] = useThemeFlavour() | ||
const [themeFlavour, setThemeFlavour] = hooks.useThemeFlavour() | ||
return pipe( | ||
theme, | ||
E.fold( | ||
LV.fold2( | ||
() => { | ||
console.error('wrong value stored in localStorage!') | ||
console.error("wrong value stored in localStorage!") | ||
}, | ||
themeFlavour => { | ||
(themeFlavour) => { | ||
switch (themeFlavour) { | ||
@@ -72,4 +78,4 @@ case "light": { | ||
} | ||
} | ||
) | ||
}, | ||
), | ||
) | ||
@@ -80,4 +86,7 @@ } | ||
``` | ||
## 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). | ||
@@ -105,5 +114,5 @@ 2. the value is present, but it is wrong (correctness). | ||
``` | ||
It also has instances for some of the most common type-classes | ||
and you can use it in the same way you usually use your usual `fp-ts` abstractions: | ||
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: | ||
```tsx | ||
@@ -127,3 +136,7 @@ // LoginLayout.tsx | ||
token, | ||
LV.fold(() => null, () => null, () => <>{ children }</>), // N.B. when you don't want to deal with the "incorrect" cases, you can use fold2 and only define two handling funcitons | ||
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 | ||
) | ||
@@ -141,3 +154,3 @@ } | ||
React.useEffect(() => { | ||
if (LV.isValid) { | ||
if (LV.isValid(token)) { | ||
goToHomePage() | ||
@@ -149,6 +162,4 @@ } | ||
<Form | ||
onSubmit={ | ||
(formValues) => api | ||
.getToken(formValues) | ||
.then(t => setToken(t)) | ||
onSubmit={(formValues) => | ||
api.getToken(formValues).then((t) => setToken(t)) | ||
} | ||
@@ -161,25 +172,49 @@ /> | ||
## 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 shape of the decoding error, `B` is the shape of the runtime error and `string` is the type resulting after encoding. | ||
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. | ||
--- | ||
> N.B. this is only useful if you use `io-ts`. | ||
If you use `io-ts` you can simply create a layer to convert `io-ts` codecs to `Codec` compliant instances: | ||
As this kind of string conversion is very often just a JSON stringification of your encoded value, we export a utility that conveniently transform `io-ts` codecs into valid ones by first applying your encoding and then stringifying the result: | ||
```ts | ||
import { fromIoTsCodec } from "react-localstorage-ts/io-ts" | ||
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 "./LocalValue" | ||
import { Codec } from "./Codec" | ||
const WrongCodec = t.type({ s: t.string, d: DateFromISOString }) | ||
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)), | ||
} | ||
} | ||
const CorrectCodec = fromIoTsCodec(WrongCodec) | ||
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) | ||
} | ||
``` | ||
## updating localstorage from outside react components | ||
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 `getLocalElement`, `setLocalElement` and `removeLocalElement`. | ||
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). | ||
@@ -190,2 +225,3 @@ - your code must be formatted using prettier. | ||
## release flow | ||
[here](https://github.com/semantic-release/semantic-release/blob/1405b94296059c0c6878fb8b626e2c5da9317632/docs/recipes/pre-releases.md) you can find an explanation of the release flow. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
19611
216