是否可以在多个组件上使用相同的 <Context.Provider> ?

unt*_*led 11 reactjs react-context

我知道我可以用 my 包装 HOC<Context.Provider>并在所有子组件中使用它。

我想在两个单独的组件中使用上下文,但它们嵌套在某个很深的地方,并且它们最近的父级位于应用程序根目录中的某个地方。我不想为(几乎)所有组件提供上下文,所以我想知道是否可以只包装这两个组件?

我尝试这样做,但只有第一个组件获得上下文。

应用程序结构如下所示:

<App>
    <A1>
        <A2>
            <MyContext.Provider>
                <Consumer1/>
            </MyContext.Provider>
        </A2>
    </A1>
    <B1>
        <B2>
            <MyContext.Provider>
                <Consumer2/>
            </MyContext.Provider>
        </B2>
    </B1>
</App>

Run Code Online (Sandbox Code Playgroud)

编辑:我错误地认为包装根组件将使上下文更改时重新渲染所有子组件。只有消费者才会重新渲染,因此包装根组件是完全可以的。

Nic*_*wer 7

如果您希望拥有在应用程序的多个部分之间共享的单个值,那么您需要以某种形式将该值移动到需要使用该值的组件的共同祖先组件。正如您在评论中提到的,您的问题是性能问题之一,并试图不重新渲染所有内容。拥有两个提供者对此并没有真正的帮助,因为仍然需要一些组件来确保两个提供者提供相同的值。因此该组件最终需要成为两个提供者的共同祖先。

相反,您可以使用shouldComponentUpdate(对于类组件)或React.memo(对于功能组件)来阻止重新渲染过程沿着组件树向下进行。使用 Context.Consumer 的深层后代仍将重新渲染,因此您可以跳过树的中间部分。这是一个示例(注意中间组件上使用 React.memo):

const Context = React.createContext(undefined);

const useCountRenders = (name) => {
  const count = React.useRef(0);
  React.useEffect(() => {
    count.current++;
    console.log(name, count.current);
  });
}

const App = () => {
  const [val, setVal] = React.useState(1);
  useCountRenders('App');
  
  React.useEffect(() => {
    setTimeout(() => {
      console.log('updating app');
      setVal(val => val + 1)
    }, 1000);
  }, [])
  
  return (
   <Context.Provider value={val}>
     <IntermediateComponent />
   </Context.Provider>
  );
}

const IntermediateComponent = React.memo((props) => {
  useCountRenders('intermediate');
  return (
    <div>
      <Consumer name="first consumer"/>
      <UnrelatedComponent/>
      <Consumer name="second consumer"/>
    </div>
  );
})

const Consumer = (props) => {
  useCountRenders(props.name);
  return (
    <Context.Consumer>
      {val => {
        console.log('running consumer child', props.name);
        return <div>consuming {val}</div>
      }}
    </Context.Consumer>
  )
}

const UnrelatedComponent = (props) => {
  useCountRenders('unrelated');
  return props.children || null;
}


ReactDOM.render(<App />, document.getElementById('root'));
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.development.js"></script>
<div id="root"></div>
Run Code Online (Sandbox Code Playgroud)

当您运行上述代码时,请检查日志以查看哪些组件重新呈现。在第一遍中,所有内容都会呈现,但一秒钟后,当应用程序状态发生变化时,只有应用程序重新呈现。IntermediateComponent、UnrelatedComponent、甚至 Consumer 都不会重新渲染。Context.Consumer 内部的函数会重新运行,并且该函数返回的任何内容(在本例中只是一个 div)都将重新渲染。