Skip to content

Instantly share code, notes, and snippets.

@ribeirotomas1904
Created January 17, 2022 12:50
Show Gist options
  • Save ribeirotomas1904/24856c3c85baf4c470c965d8634b02e8 to your computer and use it in GitHub Desktop.
Save ribeirotomas1904/24856c3c85baf4c470c965d8634b02e8 to your computer and use it in GitHub Desktop.
// fsharplint:disable MemberNames
namespace Feliz.UsePersist
open Feliz
open Browser.WebStorage
open Thoth.Json
[<RequireQualifiedAccess>]
type StorageOption =
| Local
| Session
[<AutoOpen>]
module ReactHookExtensions =
let inline private getStorage (storageOption: StorageOption option) : Browser.Types.Storage =
match storageOption with
| None
| Some StorageOption.Local -> localStorage
| Some StorageOption.Session -> sessionStorage
let inline private getInitial (storage: Browser.Types.Storage) (key: string) (initial: 'a) =
storage.getItem key
|> Decode.Auto.fromString
|> function
| Ok item -> item
| Error _ -> initial
let inline private getInitializer (storage: Browser.Types.Storage) (key: string) (initializer: unit -> 'a) =
storage.getItem key
|> Decode.Auto.fromString
|> function
| Ok item -> fun () -> item
| Error _ -> initializer
// Work on a better name
// Should i use [<Hook>] on inlined functions that deal with state?
// Should i annotate just the minimum required types?
let inline private stateSubscriber (storage: Browser.Types.Storage) (key: string) (state: 'a) : unit =
React.useEffect (
(fun () ->
Encode.Auto.toString (0, state)
|> fun x -> storage.setItem (key, x)),
[| box state |]
)
type React with
// Should i use [<Hook>] on inlined functions that deal with state?
[<Hook>]
static member inline usePersist(key: string, initial: 'a, ?storageOption: StorageOption) : 'a * ('a -> unit) =
let storage = getStorage storageOption
let initial': 'a = getInitial storage key initial
let (state, setState) = React.useState initial'
stateSubscriber storage key state
state, setState
[<Hook>]
static member inline usePersist
(
key: string,
initializer: unit -> 'a,
?storageOption: StorageOption
) : 'a * ('a -> unit) =
let storage = getStorage storageOption
let initializer': unit -> 'a = getInitializer storage key initializer
let (state, setState) = React.useState initializer'
stateSubscriber storage key state
state, setState
[<Hook>]
static member inline usePersistWithUpdater
(
key: string,
initial: 'a,
?storageOption: StorageOption
) : 'a * (('a -> 'a) -> unit) =
let storage = getStorage storageOption
let initial': 'a = getInitial storage key initial
let (state, setState) = React.useStateWithUpdater initial'
stateSubscriber storage key state
state, setState
// Error because React.useStateWithUpdater cannot use lazy initialization yet
// [<Hook>]
// static member inline usePersistWithUpdater
// (
// key: string,
// initializer: unit -> 'a,
// ?storageOption: StorageOption
// ) : 'a * (('a -> 'a) -> unit) =
// let storage = getStorage storageOption
// let initializer': unit -> 'a = getInitializer storage key initializer
// let (state, setState) =
// React.useStateWithUpdater<'a> initializer'
// stateSubscriber storage key state
// state, setState
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment