【问题标题】:How to avoid unexpected rendering while using React Context?使用 React Context 时如何避免意外渲染?
【发布时间】:2020-05-31 17:34:26
【问题描述】:

我的提供程序下有两个功能组件,

SubApp1SubApp2 当我在 SubApp1 中增加 counter1 时,SubApp2 也在渲染,即使它不需要重新渲染。

当我在 SubApp2 中增加 counter2 时,SubApp1 也在渲染。

我知道这种情况经常发生,但是如何避免这种情况呢?

App.js:

import React, {useContext, useState, memo} from "react";
import "./styles.css";


export const MainContext = React.createContext();

export const MainProvider = ({children})=> {

  const [counter1, setCounter1] = useState(0);
  const [counter2, setCounter2] = useState(0);


  return (
    <MainContext.Provider value={{
      counter1, setCounter1,
      counter2, setCounter2,
    }}>
      {children}
    </MainContext.Provider>
  );
}

export const SubApp1 = memo(()=> {
  const {counter1, setCounter1} = useContext(MainContext);
  console.log('Counter 1: ', counter1);
  return (
    <div className="App">
          <button onClick={()=> {
            setCounter1(counter1+1);
          }}>
            Increase Count 1
          </button>
    </div>
  );
});

export const SubApp2 = memo(()=> {

  const {counter2, setCounter2} = useContext(MainContext);

  console.log('counter2: ', counter2);

  return (
    <div className="App">
          <button onClick={()=> {
            setCounter2(counter2+1);
          }}>
            Increase Count 2
          </button>
    </div>
  );
});


export default function App ({navigation}){


  console.log('App Is rendering...');



  return (
    <div className="App">

         <button onClick={()=> {
            navigation.navigate('SubApp1');
          }}>
            navigate to SubApp1
          </button>

          <button onClick={()=> {
            navigation.navigate('SubApp2');
          }}>
            navigate to SubApp2
          </button>

    </div>
  );
}

index.js:

import React from "react";
import ReactDOM from "react-dom";

import App, {MainProvider} from "./App";

const MainApp = ()=> (
  <MainProvider>
    <App />
  </MainProvider>
);
const rootElement = document.getElementById("root");
ReactDOM.render(<MainApp />, rootElement);

【问题讨论】:

    标签: javascript reactjs react-native react-navigation-v5


    【解决方案1】:

    您应该将计数器作为道具传递给子应用。然后 memo 会注意只有改变 props 的组件才会被重新渲染。

    类似这样的:

    export const Wrapper1 = ()=> {
      const {counter1, setCounter1} = useContext(MainContext);
      return (
        <SubApp1 {...{counter1, setCounter1}} />
      );
    };
    
    export const SubApp1 = memo(({counter1, setCounter1})=> {
    
      console.log('Counter 1: ', counter1);
      return (
        <div className="App">
              <button onClick={()=> {
                setCounter1(counter1+1);
              }}>
                Increase Count 1
              </button>
        </div>
      );
    });
    
    export const SubApp2 = memo(({counter2, setCounter2})=> {
      console.log('counter2: ', counter2);
    
      return (
        <div className="App">
              <button onClick={()=> {
                setCounter2(counter2+1);
              }}>
                Increase Count 2
              </button>
        </div>
      );
    });
    
    
    export default function App (){
    
      const {counter2, setCounter2} = useContext(MainContext);
    
      console.log('App Is rendering...');
    
      return (
        <div className="App">
            <Wrapper1/>
            <SubApp2 {...{counter2, setCounter2}} />
        </div>
      );
    }
    

    Codesandbox 链接不对...

    【讨论】:

    • 非常感谢,但是有一个问题,我只是把我的问题简单化了。在主代码中实际上我正在使用导航,例如,我通过导航从 App 转到 SubApp1SubApp2,而 v4 导航没有警告将 setCount 作为参数发送,但在 react-navigation 5 他们不建议将函数作为参数发送。
    • 我还没有使用 react-navigation。将您的子应用程序包装在一个处理上下文的瘦包装器组件中怎么样。这样,只有轻量级包装器将被重新渲染。即使重新渲染其父级,备忘录也会阻止重新渲染。
    • 我不知道怎么把它放在一个包装器组件中
    • 非常感谢,这对我有帮助。是的,我删除了代码框链接,因为没有导航。
    【解决方案2】:

    我遵循 Peter Ambruzs 的提示,但如果我将 counter1 作为参数传递,我会遇到问题。组件不断重新渲染。

    但是,如果我只通过 setCounter1 函数,它就可以正常工作。

    下面是我使用打字稿的例子。

    const Campaigns = (): JSX.Element => {
      const { setAlert } = useContext(AlertContext);
      return <InnerCampaign {...{ setAlert }} />;
    };
    
    const InnerCampaign = memo(
      ({ setAlert }: any): JSX.Element => {...},)
    
    export default Campaigns;
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-12-21
      • 1970-01-01
      • 1970-01-01
      • 2020-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多