Skip to content

Repository files navigation

@lfades/atom

Straightforward state management library for React. You can learn more about it at atom.lfades.com.

Installation

Install the package with your package manager of choice:

pnpm add @lfades/atom
npm install @lfades/atom
yarn add @lfades/atom

Now you can create an atom and subscribe to it:

import{atom,useAtom}from"@lfades/atom"constcounterAtom=atom(0)constCounter=()=>{const[count,setCount]=useAtom(counterAtom)return(<div><h1>Counter: {count}</h1><buttononClick={()=>setCount(count+1)}>Increment</button><buttononClick={()=>setCount(count-1)}>Decrement</button></div>)}exportdefaultCounter

That's it! It only takes a few minutes to understand what the library does so I encourage you to read the source code.

API

atom

functionatom<Value>(initialValue: Value): Atom<Value>

Creates an atom with the given initialValue.

import{atom}from"@lfades/atom"constcounterAtom=atom(0)

You can read the value of the atom without subscribing to it by using the get method:

atom.get()// 0

Similarly, you can update the value of the atom with set:

atom.set(1)atom.get()// 1

When you update the value of the atom, all components subscribed to it will re-render.

useAtom

functionuseAtom<Value>(atom: Atom<Value>): [Value,(value: Value)=>void]

Returns the current value of the atom and a setter function to update it. This also subscribes the component to the atom, so it will re-render when the atom value changes.

The setter returned by useAtom is equivalent to atom.set. So the following are equivalent:

import{useAtom}from"@lfades/atom"const[count,setCount]=useAtom(counterAtom)// ..setCount(1)setCount===counterAtom.set// true
constcount=useAtom(counterAtom)[0]// ..counterAtom.set(1)

Creating an atom inside a component

This is a valid use case, but be sure to add useMemo to prevent the atom from being recreated on every render:

constcounterAtom=useMemo(()=>atom(0),[])const[count,setCount]=useAtom(counterAtom)

An atom created this way will work similarly to useState. However, you can pass down the atom through props and allow other components to subscribe to it if needed. This can prove particularly useful when combined with React Context.

useSubscribe

functionuseSubscribe<Value>(atom: Atom<Value>,cb: SubFn<Value>,deps?: DependencyList,): void

Subscribes to the atom and calls the callback function with the new value whenever it changes.

import{useSubscribe}from"@lfades/atom"useSubscribe(counterAtom,(value)=>{console.log(value)})

If the callback function has dependencies, you can pass them as the third argument:

useSubscribe(counterAtom,(value)=>{console.log(value,dep)},[dep],)

Advanced patterns

Multiple atoms in a single provider

If you want a single provider that exposes many related atoms, create them together and export focused hooks.

import{atom,useAtom}from"@lfades/atom"import{createContext,typeReactNode,useContext,useEffect,useMemo,}from"react"typeAppConfig={userId: stringtheme: "light"|"dark"showHints: boolean}constcreateAtoms=(config: AppConfig)=>({userIdAtom: atom(config.userId),themeAtom: atom(config.theme),showHintsAtom: atom(config.showHints),})constAppAtomsContext=createContext<ReturnType<typeofcreateAtoms>|null>(null,)exportfunctionAppAtomsProvider({
children,
config,}: {children: ReactNodeconfig: AppConfig}){constatoms=useMemo(()=>createAtoms(config),[])// Optionally subscribe to atoms here to trigger side effects from changes.useEffect(()=>{const{ themeAtom, showHintsAtom }=atomsconstunsubs=[themeAtom.sub((theme)=>{console.log("theme changed",theme)}),showHintsAtom.sub((showHints)=>{console.log("show hints changed",showHints)}),]return()=>{for(constunsubofunsubs){unsub()}}},[atoms])return(<AppAtomsContext.Providervalue={atoms}>{children}</AppAtomsContext.Provider>)}functionuseAppAtoms(){constatoms=useContext(AppAtomsContext)if(!atoms){thrownewError("useAppAtoms must be used within AppAtomsProvider")}returnatoms}exportfunctionuseTheme(){returnuseAtom(useAppAtoms().themeAtom)}exportfunctionuseShowHints(){returnuseAtom(useAppAtoms().showHintsAtom)}

Single atom provider with createAtomContext

If you only need a single atom, the createAtomContext utility can generate a provider and hooks for you.

import{createAtomContext}from"@lfades/atom/utils"const[CounterProvider,useCounter,useCounterAtom]=createAtomContext<number>()exportfunctionCounterRoot({ initial, children }){// `sync` keeps the atom value in sync with `initial`.return(<CounterProvidervalue={initial}sync>{children}</CounterProvider>)}exportfunctionCounter(){const[count,setCount]=useCounter()return<buttononClick={()=>setCount(count+1)}>{count}</button>}

Contributing

After cloning the repository, install dependencies with pnpm:

pnpm install

Run the main website:

pnpm dev

And then open the site at http:localhost:3000 and test your changes in the counter demo.

Testing the library with a different app

Alternatively, you can link the package and use it with an app outside the monorepo. First navigate to the package directory:

cd packages/atom

and then create a link for the package:

pnpm link --global

You can install the package in an app with:

pnpm link @lfades/atom

To remove the linked package run the following command:

pnpm uninstall --global @lfades/atom

Releasing a new version

After you're done with your changes, run:

pnpm changeset

And add a good description of your changes.

About

Straightforward state management library for React.

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages