使用 React Context 如何避免意外渲染?

How to avoid unexpected rendering while using React Context?

我的提供商有两个功能组件,

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

您应该将计数器作为道具传递给子应用程序。然后 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 link 不对...

我遵循 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;