组件级React状态管理

对于组件级别的状态共享,通过 ProvideruseContext 就能实现状态共享,但是还是有些问题

Provider 中的 value 不能每次都改变,不然会触发额外的渲染

const [value, setValue] = useState(1);

// ❌ 错误
return (
  
    
  
)

好一点的做法是用 useMemo 等包裹一下

const [value, setValue] = useState(1);
const globalValue = useMemo(() => { return { count: value } }, [value]);

// ✅ 正确
return (
  
    
  
)

光有属性还不行,还要把 setter 也放进去

const [value, setValue] = useState(1);
const globalValue = useMemo(() => {
  return { count: value, setCount: setValue }
}, [value]);

// ✅ 正确
return (
  
    
  
)

那么当状态多了以后,放很多 get 和 set 就很麻烦。于是就想到了 useReducer ,这样你就不得不写一大堆代码

  • context.js
  • reducer.js
  • model.js

对于一个组件来说,是不是杀鸡用牛刀了...

所以需要一个轻量级的组件共享方案

Jotai

Jotai 是个受 recoil 启发而开发的状态管理库,它能够用较少的代码做到组件中状态共享。

用法上和普通的 useState 很像,比如

import React from 'react';
import { atom, useAtom } from 'jotai';

const countAtom = atom(0);

export default function(props) {
  const [count, setCount] = useAtom(countAtom);

  return (
    

{count}

) }

注意到上面的 useAtom 函数里需要用到一个 atom 函数。什么是 atom 函数 ? 官方是这么解释的

An atom represents a piece of state.

也不用太过纠结,就当是个 model 定义方法。通过这种方式,在任意组件内部都能访问到这个变量,很方便

atoms

Atom

定义一个 computed atom 。

const doubledCountAtom = atom((get) => get(countAtom) * 2)

const count1 = atom(1)
const count2 = atom(2)
const count3 = atom(3)
const sum = atom((get) => get(count1) + get(count2) + get(count3))

定义一个写方法的 atom

const decrementCountAtom = atom(
  (get) => get(countAtom),
  (get, set, _arg) => set(countAtom, get(countAtom) - 1),
)

function Counter() {
  const [count, decrement] = useAtom(decrementCountAtom)
  return (
      
  )
}

配合 Immer

假设组件里的状态多了,一个个定义 atom 会很麻烦,这时候可以考虑使用 object 来当状态。

更新的时候可以考虑搭配 immer 来使用,Jotai 默认就支持 immer 。

import { useAtom } from 'jotai'
import { atomWithImmer } from 'jotai/immer'

const countAtom = atomWithImmer(0)

const Controls = () => {
  const [count, setCount] = useAtom(countAtom)
  // setCount === update : (draft: Draft) => void
  const inc = () => setCount((c) => (c = c + 1))
  return (
    
{count}
) }