网站搜索

reactjs - 在 React 上下文中传递多个状态


我有一个上下文,需要在树中传递多个状态。设置 Provider 的 value 的正确方法是什么?

  1. 在原地创建对象不起作用,因为它在每次重新渲染时都会被重建,即使上下文的实际内容没有变化,也会导致子组件重新渲染(因为创建了一个新对象,内存地址不同,导致子组件认为对象已经改变)。

我应该如何传递上下文?

//Example:
interface Context {
 state1: number,
 state2: number,
}

const MyContext = React.createContext<Context|null>(null)

const MyFC: React.FC = (props)=>{
 const [state1,setState1] = useState<number>(0) //To pass down
 const [state2,setState2] = useState<number>(0) //To pass down
 const [state3,setState3] = useState<number>(0) //Not to pass down

 return(
   <MyContext.Provider value={????}>
      {/* components */}
   <MyContext>
 )
}

答案#1

你可以将一个键值数组传递给 value:

value={{'state1':[state1,setstate1],'state2':[state2,setstate2]}}

然后你可以检索每个数据:

const context = React.useContext(MyContext);
const [state1,setstate1] = context['state1'];
const [state2,setstate2] = context['state2'];

或者直接:

const [state1,setstate1] = React.useContext(MyContext)['state1'];
const [state2,setstate2] = React.useContext(MyContext)['state2'];

你可以添加任意数量的州。

答案#2

在上下文中传递复杂状态的选项有多种。

选项 1:将不一起变化的上下文分开

如果构成上下文的元素关系不紧密,考虑 将上下文拆分为不同的上下文。这样,你可以传递单一状态,而无需处理“统一”状态的问题。

选项 2:使用 useReducer 来组合相关状态

在使用 useState 钩子时,建议为每个元素创建一个单独的状态,因为与类组件中的状态不同,更新状态的单个元素可能会导致不必要的复杂性。

useState 是在函数组件中保存状态最常用的工具。

然而,当需要更复杂的状态(和状态管理)时,可以使用 useReducer 将状态封装在一个对象中。然后你可以将 reducer 的状态作为 context 的值传递

function reducer(state,action) {
 switch(action.type){
   //Cases:
   //.... (works like redux reducer)
   default:
     return state
 }
}

const MyContext = React.createContext<Context|null>(null)

const MyFC: React.FC = (props)=>{
 const [compressedState,dispatch] = useReducer(reducer,{state1:0,state2:0})
 const [state3,setState3] = useState<number>(0) //Not to pass down

 return(
   <MyContext.Provider value={compressedState}>
       {/* components */}
   <MyContext>
 )
}

选项 3:useMemo

当之前的选项都不可行时,useMemo 是可行的方式。只需将所有需要传递的元素组合起来,以创建一个只有在至少一个依赖项发生变化时才会更新的值。

const MyContext = React.createContext<Context|null>(null)

const MyFC: React.FC = (props)=>{
 const [state1,setState1] = useState<number>(0) //To pass down
 const [state2,setState2] = useState<number>(0) //To pass down
 const [state3,setState3] = useState<number>(0) //Not to pass down

 const contextValue= useMemo(()=>{
   return{
    state1: state1,
    state2: state2
   }
 },[state1,state2]);

 return(
   <MyContext.Provider value={contextValue}>
       {/* components */}
   <MyContext>
 )
}

关于 useMemo 的最后说明 当我们需要传递 useState 的两个元素(状态和设置状态)或 useReducer 的两个元素(状态和分发)时,useMemo 也会变得非常有用。

在上下文中,我们应该始终 避免 在 value 属性中放入内联对象。正如文章开头所说,这个对象在每次渲染时都会重新创建(新的内存地址),导致检查它的钩子也会重新渲染(因为观察到的内存值发生了变化),并且这种重新渲染会一直传递到小部件树下方。

在这种情况下使用 memo 会非常有用,因为它可以避免传递内联对象:

//Example for useReducer. Works also with useState.

const contextValue = useMemo(() => {
  return { state, dispatch };
}, [state, dispatch]);

来源:

  • https://github.com/facebook/react/issues/15156
  • https://hswolff.com/blog/how-to-usecontext-with-usereducer/