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

@fattafatta/rescript-jotai

v0.4.2

Published

Rescript bindings for Jotai (react state management).

Downloads

360

Readme

rescript-jotai

ReScript bindings for Jotai. Primitive and flexible state management for React.

Versions below 0.3.0 support Jotai v1.

Versions 0.3.0 and higher support Jotai v2. They also require at least rescript 10.1 (for async/await) and react 18+.

Versions 0.4.0 and higher support Jotai v2 with rescript 11 and react 18+.

Installation

Install with npm:

npm install jotai @fattafatta/rescript-jotai

Or install with yarn:

yarn add jotai @fattafatta/rescript-jotai

Add @fattafatta/rescript-jotai as a dependency to your bsconfig.json:

"bs-dependencies": ["@rescript/react", "@fattafatta/rescript-jotai"]

Usage

Provider

A Provider works just like React context provider. If you don't use a Provider, it works as provider-less mode with a default store. A Provider will be necessary if we need to hold different atom values for different component trees. The store property is optional

let store = Jotai.Store.make()

module App = {
  @react.component
  let make = () =>
    <Jotai.Provider store={store}>
      ...
    </Jotai.Provider>
}

Store

Atoms are always stored in a store. If no specific store is passed to Provider a default store will be created.

Create a store (Jotai.Store.make)

Creates a store.

let store = Jotai.Store.make();

Get default store (Jotai.Store.getDefaultStore)

Get the default store that is created if no specific store was provided.

let store = Jotai.Store.getDefaultStore();

Access a store

A store supports 3 functions to access its content.

get for getting atom values.

let value = Jotai.Store.get(store, atom);

set for setting atom values.

Jotai.Store.set(store, atom, 1);

sub for subscribing to atom changes. Returns a function to unsubscribe.

let unsub = Jotai.Store.sub(store, atom, () => {
  Js.Console.log2("atom value is changed to", Jotai.Store.get(store, atom))
})

// unsub() to unsubscribe

Create atoms

In Jotai there is no distinction between normal and async atoms (i.e. atoms that hold promise). So in Jotai hooks will always return the resolved value. It's not possible to reproduce this one to one in ReScript. Therefore, wherever necessary, functions are provided for normal atoms, and separate *Async functions are provided to handle atoms with promises. (e.g. useAtom and useAtomAsync).

CAUTION: Using the wrong function may not result in compile errors, but very likely runtime errors. As a simple guideline just remember that a hook should never return a promise. If the return type is promise<> use the async version of the function instead.

Atom type

Atoms have a value, a setter function (from Atom.Actions), and a set of tags that restrict which operations are allowed on the atom (e.g is the atom #resettable). Normally the type will be inferred automatically. If annotation is required it should be sufficient to provide the first type (the value).

Example:

let atom: Jotai.Atom.t<int, _, _> = Jotai.Atom.make(1)

Primitive atom (Jotai.Atom.make)

Create a (primitive) readable and writable atom (config).

CAUTION: Don't pass a function as argument to Atom.make. That would implicitly create a computed atom and the compiler will produce weird types. Use Atom.makeComputed for that.

let atom1 = Jotai.Atom.make(1)
let atom2 = Jotai.Atom.make(["text"])
// DON'T do this:
let atom3 = Jotai.Atom.make(() => 1)

Atom from thunk (Jotai.Atom.makeThunk)

Create a readonly atom from a function. The function can be async.

let atom1 = Jotai.Atom.makeThunk(async () => 1)
// shorthand for
let atom2 = Jotai.Atom.makeComputed(async ({get}) => 1)

Computed atom (Jotai.Atom.makeComputed)

Create a computed readonly atom. A computed atom can combine any number of readable atoms to create a single derived value. The syntax varies slightly from Jotai. Note the curly braces in ({get}). Requires React.Suspense or Utils.Loadable if the value is a promise (e.g. the getter is async)

let atom1 = Jotai.Atom.make(1)
let atom2 = Jotai.Atom.makeComputed(({get}) => get(atom1) + 1)
let atom3 = Jotai.Atom.makeComputed(({get}) => get(atom1) + get(atom2) + 1)

// using async atoms requires React.Suspense
let atom4 = Jotai.Atom.makeComputed(async ({get}) => await get(asyncAtom) + 1)

(DEPRECATED) Computed async atom (Jotai.Atom.makeComputedAsync)

This function is no longer necessary. Atoms no fully support async getters out of the box.

(Requires React.Suspense) Create an computed readonly atom with an async getter. All components will be notified when the returned promise resolves.

let atom1 = Jotai.Atom.make(1)
let atom2 = Jotai.Atom.makeComputedAsync(async ({get}) => {atom1->get + 1})

Computed writable atom (Jotai.Atom.makeWritableComputed)

Create a computed atom that supports read and write. The getter may by async, but the setter must be synchronous. For async setters use makeComputedAsync.

let atom1 = Jotai.Atom.make(1)
let atom2 = Jotai.Atom.makeWritableComputed(
  ({get}) => get(atom1) + 1,
  ({get, set}, arg) => {
    atom1->set(get(atom1) + arg)
  },
)

Computed writable async atom (Jotai.Atom.makeWritableComputedAsync)

Create a computed atom with asynchronous write (setter). Jotai supports async write operations for computed atoms. Simply call 'set' when the promise resolves.

let atom1 = Jotai.Atom.make(1)
let atom2 = Jotai.Atom.makeWritableComputedAsync(
  ({get}) => get(atom1) + 1,
  async ({get, set}, arg) => {
    // do async stuff
    set(atom1, get(atom1) + arg)
  },
)

Computed writeonly atom (Jotai.Atom.makeWriteOnly)

Create a writeOnly computed atom. (Note: Sometimes the type can not be inferred automatically and has to be annotated)

let atom1 = make(1)
let atom2: Jotai.Atom.t<int, _, _> = Jotai.Atom.makeWriteOnly(({get, set}, args) =>
  atom1->set(get(atom1) + args)
)

Computed writeonly async atom (Jotai.Atom.makeWriteOnlyAsync)

Create a writeOnly computed async atom (i.e. the setter is an async function)

let atom1 = make(1)
let atom2 = Jotai.Atom.makeWriteOnlyAsync(async ({get, set}, args) => atom1->set(get(atom1) + args))

OnMount (Jotai.Atom.onMount)

onMount is a function which takes a function setAtom and returns onUnmount function optionally. The onMount function is called when the atom is first used in a provider, and onUnmount is called when it’s no longer used.

let atom1 = Jotai.Atom.make(1)
atom1->Jotai.Atom.onMount(setAtom => {
  setAtom(a => a + 1) // increment count on mount
  () => () // return onUnmount function
})

Core hooks

Using read/write atoms (Jotai.Atom.useAtom)

Standard hook to use with read/write synchronous atoms. (For handling of readOnly/writeOnly atoms see Jotai.useAtomValue or Jotai.useSetAtom)

let atom1 = Jotai.Atom.make(1)
let (value, setValue) = Jotai.Atom.useAtom(atom1)

Using async read/write atoms (Jotai.Atom.useAtomAsync)

Standard hook to use with read/write async atoms (i.e. all atoms that contain a promise). (For handling of readOnly/writeOnly atoms see Jotai.useAtomValueAsync or Jotai.useSetAtom)

let atom1 = Jotai.Atom.makeAsync(async () => 1)
let (value, setValue) = Jotai.Atom.useAtomAsync(atom1)

Get only the update function (Jotai.Atom.useSetAtom)

A hook that returns only the update function of an atom. Can be used to access writeOnly atoms.

let atom = Jotai.Atom.make(1)
let setValue = Jotai.Atom.useSetAtom(atom)
setValue(prev => prev + 1)

Get only the value (Jotai.Atom.useAtomValue)

A hook that returns only the value of a synchronous atom. Can be used to access readOnly atoms.

let atom = Jotai.Atom.make(1)
let value = Jotai.Atom.useAtomValue(atom)

Get the value from an async atom (Jotai.Atom.useAtomValueAsync)

A hook that returns only the value of an async atom (i.e. the atom contains a promise). Can be used to access readOnly async atoms.

let atom = Jotai.Atom.make(1)
let value = Jotai.Atom.useAtomValue(atom)

Utils

Atom with localStorage (Jotai.Utils.AtomWithStorage.make)

Creates an atom with a value persisted in localStorage. Currently only localStorage is supported.

let atom1 = Jotai.Utils.AtomWithStorage.make('storageKey', 1)

Resettable atom (Jotai.Utils.AtomWithReset.make)

Creates an atom that can be reset to its initialValue with the useResetAtom hook.

let atom = Jotai.Utils.AtomWithReset.make(1)
// ... change value ...
let reset = Jotai.Utils.useResetAtom(atom)
reset()

Atom with default (Jotai.Utils.AtomWithDefault.make)

Create a resettable, writable atom. Its default value can be specified with a read function instead of an initial value. This function support sync and async getters.

let atom1 = Jotai.Atom.make(1)
let atom2 = Jotai.Utils.AtomWithDefault.make(({get}) => atom1->get + 1)
// async
let atom3 = Jotai.Atom.makeAsync(async () =>1)
let atom4 = Jotai.Utils.AtomWithDefault.make(async({get}) => await atom3->get + 1)

Atom with Reducer (Jotai.Utils.AtomWithReducer.make)

Creates an atom that uses a reducer to update its value.

type actionType = Inc(int) | Dec(int)
let countReducer = (prev, action) => {
  switch action {
  | Inc(num) => prev + num
  | Dec(num) => prev - num
  }
}
let atom = Utils.AtomWithReducer.make(0, countReducer)
let (value, dispatch) = Atom.useAtom(atom)
Inc(1)->dispatch

AtomFamily (Jotai.Utils.AtomFamily)

Creates an atomFamily. If the compiler has trouble inferring the type, it is recommended to set the type directly on the function param.

let atomFamily = Jotai.Utils.AtomFamily.make((name: string) => Jotai.Atom.make(name))
let atom = atomFamily("text")
With Equals function

Creates an atomFamily with a supplied comparison function

let atomFamWithEqual = Jotai.Utils.AtomFamily.makeWithEqual(
  name => Jotai.Atom.make(name),
  (strA, strB) => strA == strB,
)
Remove

Removes an atom from an atomFamily.

Jotai.Utils.AtomFamily.remove(atomFamily, "text")
SetShouldRemove (Jotai.Utils.AtomFamily.setShouldRemove)

Register a shouldRemove function.

let shouldRemove = (createdAt, param) => param == "test"
Jotai.Utils.AtomFamily.setShouldRemove(atomFamily, shouldRemove)

Unregister the shouldRemove function with Jotai.Utils.AtomFamily.setShouldRemoveUnregister.

Jotai.Utils.AtomFamily.setShouldRemoveUnregister(atomFamily, Js.Null.empty)

Loadable (Jotai.Utils.Loadable)

Can be used if you don't want async atoms to suspend or throw to an error boundary.

let atom = Jotai.Atom.makeThunk(async () => "stuff")
let loadableAtom = Jotai.Utils.Loadable.make(atom)

// inside component:
let value = Jotai.Utils.Loadable.useLoadableValue(loadableAtom)
{switch (value.state, value.data, value.error) {
  | (#hasData, Some(d), _) => <p>{("Data: " ++ d)->React.string}</p>
  | (#hasError, _, Some(e)) => <p>{e->React.string}</p>
  | _ => <p>{"Loading..."->React.string}</p>
}}

FreezeAtom (Jotai.Utils.FreezeAtom)

Creates an atom that is read-only and deeply frozen from an existing atom.

let atom = Jotai.Atom.make(1)
let freeze = Jotai.Utils.FreezeAtom.freezeAtom(atom)

SelectAtom (Utils.SelectAtom.make)

Derives a readonly atom that selects a slice of the state of an atom.

let a = Atom.make(1)
let b = Utils.SelectAtom.make(a, (a, _prev) => a + 1)

Derives a readonly atom that selects a slice of the state of an atom with a custom equality function.

let a = Atom.make(1)
let b = Utils.SelectAtom.makeWithEquality(a, (a, _prev) => a + 1, (a, b) => a == b)

AtomWithRefresh (Jotai.Utils.AtomWithRefresh.make)

Creates an atom that we can refresh, which is to force reevaluating the read function..

let atom1 = Jotai.Utils.AtomWithRefresh.make(_ => 1)
let (value, refresh) = Jotai.Utils.useRefreshAtom(atom1)
refresh()

Creates a writeable atom that we can refresh, which is to force reevaluating the read function..

let atom1 = Jotai.Utils.AtomWithRefresh.makeComputed(
({get}) => 1,
({get, set}, arg) => {/* set something */},
)
let (value, update) = Jotai.Utils.useAtom(atom1)

Utils Hooks

Reset an atom (Jotai.Utils.useResetAtom)

Returns a function that can be used to reset a resettable atom.

let atom = Jotai.Utils.AtomWithReset(1)  // value: 1
let (_, setValue) = Jotai.Atom.useAtom(atom)
setValue(2)  // value: 2
let resetValue = Jotai.Utils.useResetAtom(atom)
resetValue()  // value back to: 1

Refresh an atom (Jotai.Utils.useRefreshAtom)

Hook to refresh an AtomWithRefresh. (This is the equivalent of calling the set function without arguments in Jotai)

let atom1 = Jotai.Utils.AtomWithRefresh.make(_ => 1)
let (value, refresh) = Jotai.Utils.useRefreshAtom(atom1)
refresh()

(deprecated) Pass a reducer to a writable atom (Jotai.Utils.useReducerAtom)

Allows to use a reducer function with a primitive atom.

type actionType = Inc(int) | Dec(int)
let countReducer = (prev, action) => {
  switch action {
  | Inc(num) => prev + num
  | Dec(num) => prev - num
  }
}
let atom = Jotai.Atom.make(0)
let (value, dispatch) = Jotai.Utils.useReducerAtom(atom, countReducer)
Inc(1)->dispatch

Use a loadable atom (Jotai.Utils.Loadable.useLoadableValue)

Hook to use a loadable atom.

// inside component:
let value = Jotai.Utils.Loadable.useLoadableValue(loadableAtom)

Alternatives

This package was greatly inspired by re-jotai. I just preferred to have a different syntax for the get/set functions.

Missing functions from Jotai

These functions are not (yet) supported.

  • atomWithObservable
  • atomWithHash
  • selectAtom
  • useAtomCallback
  • freezeAtom
  • splitAtom
  • useHydrateAtoms
  • options for useAtom
  • Vanilla library
  • multi argument setters (write functions). This can be accoplished by simly using an array or a record as argument instead.