【问题标题】:Passing multiple states in react context在反应上下文中传递多个状态
【发布时间】:2021-07-02 07:34:18
【问题描述】:

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

  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>
 )
}

【问题讨论】:

    标签: reactjs typescript react-context


    【解决方案1】:

    您可以将键控数组传递给值:

    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'];
    

    你可以添加你想要的状态。

    【讨论】:

    • 我在帖子的第 1 点中讨论了一个问题:这样,当我更新状态 3 时,键控对象将被重新构建,从而导致子对象也被重新构建(因为上下文地址发生了变化,因为它是一个新对象),即使 state1 和 state2(上下文中的那些)没有改变。这是我要“解决”的问题
    • @AndreaCostanzo1 那么这就是你应该使用useMemo钩子的地方。 You can find it discussed here on GitHub
    • @AreebKhan 你能发个帖子吗?我想留下这个帖子的答案。只是为了确认您的答案并留给读者
    • useMemouseReducer 取决于您想要传递的内容。这是一个很棒的blog post,它帮助我理解了它的工作原理。
    • @nico263nico 我试着把你所有的答案都写在一个帖子里。如果我遗漏了什么,请告诉我
    【解决方案2】:

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

    选项 1:拆分不会一起改变的上下文

    如果构成上下文的元素不紧密相关,请考虑将上下文拆分为不同的上下文。通过这种方式,您可以传递单个状态,而无需处理“统一”状态的问题。

    方案二:使用useReducer组合相关状态

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

    使用状态是功能组件中保存状态最常用的工具。

    但是,当需要更复杂的状态(和状态管理)时,可以使用 useReducer 将状态包装在单个对象中。然后你可以将 reducer 的状态作为上下文的值传递

    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 是要走的路。 只需将需要传递的所有元素组合起来,即可创建一个仅在至少有一个依赖项发生更改时才更新的值。

    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(状态和调度)的两个元素时,使用备忘录也会变得有用。

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

    在这种情况下使用备忘录可能会变得很少,因为它可以避免传递内联对象:

    //Example for useReducer. Works also with useState.
    
    const contextValue = useMemo(() => {
      return { state, dispatch };
    }, [state, dispatch]);
    

    来源:

    【讨论】:

      猜你喜欢
      • 2021-07-31
      • 1970-01-01
      • 2021-10-18
      • 2017-03-24
      • 1970-01-01
      • 1970-01-01
      • 2019-01-07
      • 2023-03-10
      • 1970-01-01
      相关资源
      最近更新 更多