0

I have two functional component under my provider,

SubApp1 and SubApp2 and here when I am increasing counter1 in SubApp1 the SubApp2 also is rendering, even when it is not need to be re-rendered.

And when I am increasing counter2 in SubApp2 the SubApp1 also is rendering.

I know this happens regally, but How can avoid this situation ?

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);
Muhammad
  • 2,572
  • 4
  • 24
  • 46

2 Answers2

2

You should pass the counter to the SubApps as props. Then memo will take care that only the component with changing props will be rerendered.

Something like this:

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 is not right...

Peter Ambruzs
  • 7,763
  • 3
  • 30
  • 36
  • Thank you very much, but there is one probem, I just made simple my problem. and in the Main code actually I am using navigation, for example I am going from App to `SubApp1` and `SubApp2` by navigating, and v4 navigation there was no warning to send the `setCount` as params, but in react-navigation 5 they not recommended to send functions as params. – Muhammad Feb 16 '20 at 18:06
  • I do not used react-navigation yet. What about wrapping your SubApps in a thin wrapper component handling the context. That way only the lightweight wrapper will be re-rendered. Memo prevents rerendering even when its parent are re-rendered. – Peter Ambruzs Feb 16 '20 at 18:12
  • I dont know How can it put it in a wrapper component – Muhammad Feb 16 '20 at 18:15
  • Thank you so much, this helped me. Yeah I removed codesandbox link because there was not navigation. – Muhammad Feb 16 '20 at 18:58
0

I follow the tip of Peter Ambruzs, but i have a problem if i pass counter1 as a param. The component keep rerendering.

But, if i pass just setCounter1 function, its works fine.

Below, my example using typescript.

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

const InnerCampaign = memo(
  ({ setAlert }: any): JSX.Element => {...},)

export default Campaigns;

Lucas Simões
  • 589
  • 5
  • 10