Original link: https://innei.in/posts/programming/jotai-experience-with-component-state-abstraction
This rendering is generated by marked, and there may be typographical problems. For the best experience, please go to: https://innei.in/posts/programming/jotai-experience-with-component-state-abstraction
Jotai is a very good atomic model based React state management library. It takes a bottom-up approach to building global React state management, building state by composing atoms, and optimizing rendering based on atomic dependencies. This solves the extra re-render of the React context and removes the need for memoization.
Using Jotai can easily split the state (State) of the top-level components into multiple Atoms, separate the state from the UI, and manage the state efficiently. You can also get the state Getter / on-demand inside the component Setter, reducing a lot of re-rendering.
Use useAtom
instead of useState
The usage of Jotai atom is very simple. Generally, you only need to write the initialValue of useState
into atom()
, and then directly replace useState
.
const isShowAtom = atom(true) const Foo = () => { const [isShow, setIsShow] = useState(true) // change to const [isShow, setIsShow] = useAtom(isShowAtom) }
In addition, Jotai also provides useAtomValue
and useSetAtomValue
, which can be used on demand. In the scenario of not consuming atomValue, you don’t need to use useAtom
, but you can use useSetAtomValue
directly. The advantage is that when the atomValue changes, the component will not be re-rendered. For example, in the following example, when the parent component clicks the Button, no re-rendering will occur, and only the Bar will be updated:
const isShowAtom = atom(true) const Foo = () => { const setIsShow = useSetAtom(isShowAtom) return <> <Bar /> <button onClick={() => setIsShow(true)}>Show</button> </> } const Bar = () => { const isShow = useAtomValue(isShowAtom) // ... }
::: info
Off-topic: If I don’t use Jotai, how should I avoid excessive multi-rendering caused by useState?
In the above example, we can use useState + useContext instead.
const IsShowContext = createContext(false) const SetIsShowContext = createContext<SetStateAction<boolean>>(() => {}) const Foo = () => { const [isShow, setIsShow] = useState(false) return ( <IsShowContext.Provider value={isShow}> <SetIsShowContext.Provider value={setIsShow}> <FooImpl /> </SetIsShowContext.Provider> </IsShowContext.Provider> ) } const FooImpl = () => { const setIsShow = useContext(SetIsShowContext) return ( <> <Bar /> <button onClick={() => setIsShow(true)}>Show</button> </> ) } const Bar = () => { const isShow = useContext(IsShowContext) // ... }
And this method of breaking the Context for performance optimization is very difficult to maintain when there are too many states.
If you don’t want to use a state library similar to Jotai, you can try foxact ‘s useContextState
implementation, which is similar to the above.
:::
Use useContext
and atom
to shrink the global state into the component
After using useAtom
to replace useState
above, the component state is externalized, so that the component cannot be reused. Once it is reused, its state is shared. We can use useContext
and atom
to shrink the state of the component to the inside of the component.
Still take the simple example above as an example.
const FooContext = createContext(null) const Foo = () => { const contextValue = useRef({ isShowAtom: atom(false), }).current return ( <FooContext.Provider value={contextValue}> <FooImpl /> </FooContext.Provider> ) } const FooImpl = () => { const { isShowAtom } = useContext(FooContext) const setIsShow = useSetAtom(isShowAtom) return ( <> <Bar /> <button onClick={() => setIsShow(true)}>Show</button> </> ) } const Bar = () => { const { isShowAtom } = useContext(FooContext) const isShow = useAtomValue(isShowAtom) // ... }
Use a context to subtly enter the state into the component again, pass in a contextValue from the top layer, mount atom
inside the context, and consume the atom on demand, and because the Context has no dependencies, you can create any component in the built-in component The use of contextValue does not need to worry about the re-rendering problem caused by the change of the contextValue, where the contextValue will never change, and thanks to Jotai, get the atom from the contextValue anywhere and then use useAtomValue
to subscribe to the change of the atom and respond to the component.
On-Demand Subscriptions to Atom – selectAtom
Jotai provides selectAtom
function, which can create a new readOnly atom based on the original atom. The main method of use is to implement selector.
If the general atomValue itself is a primitive type, there is no need to use this function. If it is a value of reference type, such as an object. According to the characteristics of Immutable, if a huge object needs to be changed internally, a new object will be created. Any changes inside the object will cause the components subscribed to the entire atomValue to be updated.
const objectAtom = atom({ type: 'object', value: { foo: 1 } }) const Foo = () => { const setter = useSetAtom(objectAtom) return <> <Bar /> <button onClick={() => setter(prev => ({...prev, value: { foo:1 }}))}>Show</button> </> } const Bar = () => { const { type } = useAtomValue(objectAtom) // ... }
In the above example, although Bar only consumes type
, after the button in Foo is clicked, Bar will also change because the value of objectAtom changes.
const objectAtom = atom({ type: 'object', value: { foo: 1, }, }) const Foo = () => { const setter = useSetAtom(objectAtom) return ( <> <Bar /> <button onClick={() => setter((prev) => ({ ...prev, value: { foo: 1 } }))} > Show </button> </> ) } const Bar = () => { const type = useAtomValue( selectAtom( objectAtom, useCallback((atomValue) => atomValue.type, []), // 注意这里), ) // ... }
However, using selectAtom to extract the internal value will not cause such a problem. Note that you need to use useCallback
to wrap the incoming selector, or extract this function to the outside of the component. You need to ensure that the function remains unchanged in the next re-rendering, which will cause re-rendering hell.
The above three points can cover most scenarios. Hurry up and try it.
finish watching? say something
This article is transferred from: https://innei.in/posts/programming/jotai-experience-with-component-state-abstraction
This site is only for collection, and the copyright belongs to the original author.