Jotai
Created: 2023-05-03 17:35:59 -0700 Modified: 2023-06-20 20:54:49 -0700
Random notes
Section titled Random notesatomFamily
Section titled atomFamilyA read-only version of an atomFamily
Section titled A read-only version of an atomFamily// This is a contrived interface just for the sake of exampleinterface Item { id: number name: string}
function newItem(id: number): Item { return { id, name: someFunctionToGetNameFromId(id) }}
const itemAtomFamily = atomFamily( (id: number) => atom(newItem(id)), (id1, id2) => id1 === id2)
// By exporting only this, no one will have access to itemAtomFamily to be able to// set items themselvesexport const itemAtomFamilyReadOnly = atomFamily((id: number) => atom((get) => get(itemAtomFamily(id))))
Read-write version of the prior example
Section titled Read-write version of the prior exampleexport const itemAtomFamilyReadOnly = atomFamily((id: number) => atom( (get) => get(itemAtomFamily(id)), (get, set, name: string) => { const item = get(itemAtomFamily(id)) set(itemAtomFamily(id), { ...item, name }) } ))
Get the next ID for some new object
Section titled Get the next ID for some new objectimport { createStore } from "jotai"
const store = createStore()
const nextIdAtom = atom(0)function getNextId(): number { const nextId = store.get(nextIdAtom) store.set(nextIdAtom, nextId + 1) return nextId}
Troubleshooting
Section titled TroubleshootingFast Refresh issue
Section titled Fast Refresh issueSee https://github.com/pmndrs/swc-jotai/issues/14.
As a workaround before realizing I’d forgotten to modify nextjs.config, I wrote a <JotaiUpdateDetector/> component. It just tells you when you’ve modified a file (or a dependency of a file) and should manually refresh.
export const usersFileUpdatedAtom = atom(true)
import { usersFileUpdatedAtom } from "@/store/User"import { postsFileUpdatedAtom } from "@/store/Posts"import { PrimitiveAtom, useAtom } from "jotai"import _ from "lodash"import { useState, useEffect } from "react"
function useJotaiUpdateNotifier(atom: PrimitiveAtom<boolean>) { const [alreadyDetectedUpdate, setAlreadyDetectedUpdate] = useState(false) const [wasFileUpdated, setFileWasUpdated] = useAtom(atom) const [needsRefresh, setNeedsRefresh] = useState(false)
useEffect(() => { if (wasFileUpdated) { if (alreadyDetectedUpdate) { setNeedsRefresh(true) } setFileWasUpdated(false) setAlreadyDetectedUpdate(true) } }, [ wasFileUpdated, setFileWasUpdated, alreadyDetectedUpdate, setAlreadyDetectedUpdate, setNeedsRefresh, ])
return needsRefresh}
export function JotaiUpdateDetector() { const [dismissed, setDismissed] = useState(false) const atomFilePairs = [ { file: "Users", atom: usersFileUpdatedAtom }, { file: "Posts", atom: postsFileUpdatedAtom }, ]
const messages = [] for (let i = 0; i < atomFilePairs.length; ++i) { const { file, atom } = atomFilePairs[i] // eslint-disable-next-line react-hooks/rules-of-hooks if (useJotaiUpdateNotifier(atom)) { messages.push(file) } }
if (_.isEmpty(messages) || dismissed) { return null }
return ( <div className="fixed flex p-4 text-4xl left-5 top-5 right-5 bg-red-900/75 text-white items-center justify-between"> ⚠ These files were updated: {JSON.stringify(messages)}. You should manually refresh the page. <button onClick={() => setDismissed(true)}> Dismiss </button> </div> )}