Reputation: 1088
I would like to know how can I avoid unnecessary updates on a child component
that does not receive props
, but uses useContext
.
I've added React.memo
to the Child2 component
for me to make some demo tests.
As you can see, when I change the input
value which uses context
to pass the data to child1
, Child2
does not re-render, as React.memo
prevents this behavior.
How can I prevent unnecessary renders in Child1 as well? I know that React.memo won't work as it needs props
, and with context
this will not happen, as no props
are being passed down to this component
App.js
import React, {useState} from 'react';
import './App.css';
import Child1 from "./Child1";
import Child2 from "./Child2";
import SillyContext from './context'
function App() {
const [name, setName] = useState('Radha');
const [count, setCount] = useState(0)
const increaseCount = () => {
setCount(prevState => prevState + 1)
}
return (
<div className="App">
<Child2 count={count}/>
<button onClick={increaseCount}>Increase</button>
<input type="text" value={name} onChange={(event => setName(event.target.value))}/>
<SillyContext.Provider value={{name}}>
<Child1/>
</SillyContext.Provider>
</div>
);
}
export default App;
Child1
import React, {useContext} from "react";
import SillyContext from './context'
export default function Child1() {
const sillyContext = useContext(SillyContext)
console.log('[Child 1 ran]')
return (
<div>
<div>[Child 1]: {sillyContext.name}</div>
</div>
)
}
Child2
import React from 'react'
export default React.memo(function Child2(props) {
console.log('[Child2 Ran!]')
return <div>[Child2] - Count: {props.count}</div>
})
Upvotes: 0
Views: 327
Reputation: 281676
The major problem due to which Child1 re-renders when count is updated is because you are passing a new object reference to Context Provider everytime.
Also If the App component re-renders, all element rendered within it re-render, unless they implement memoization or are PureComponent or use shouldComponentUpdate
You can make 2 changes to fix your re-rendering
useMemo
to memoize the object passed as value to providerApp.js
function App() {
const [name, setName] = useState("Radha");
const [count, setCount] = useState(0);
const increaseCount = () => {
setCount(prevState => prevState + 1);
};
const value = useMemo(() => ({ name }), [name]);
return (
<div className="App">
<Child2 count={count} />
<button onClick={increaseCount}>Increase</button>
<input
type="text"
value={name}
onChange={event => setName(event.target.value)}
/>
<SillyContext.Provider value={value}>
<Child1 />
</SillyContext.Provider>
</div>
);
}
Child2
const Child2 = React.memo(function(props) {
console.log("[Child2 Ran!]");
return <div>[Child2] - Count: {props.count}</div>;
});
Child 1
const Child1 = React.memo(function() {
const sillyContext = useContext(SillyContext);
console.log("[Child 1 ran]");
return (
<div>
<div>[Child 1]: {sillyContext.name}</div>
</div>
);
});
Upvotes: 1
Reputation: 11283
You can use useMemo inside Child1
component. It doesn't solve rerendering problem, but it is some kind of improvement.
Following snippet is third variant proposed by React core dev
we could make our code a bit more verbose but keep it in a single component by wrapping return value in useMemo and specifying its dependencies. Our component would still re-execute, but React wouldn't re-render the child tree if all useMemo inputs are the same.
const { useState, useEffect, createContext, useContext, useMemo } = React;
const SillyContext = createContext();
const Child2 = React.memo(({count}) => {
return <div>Count: {count}</div>
})
const Child1 = () => {
const {name} = useContext(SillyContext);
return useMemo(() => {
console.log('Child1');
return <div>
<div>Child 1: {name}</div>
</div>}, [name])
}
const App = () => {
const [count, setCount] = useState(0);
const increaseCount = () => {
setCount(prevState => prevState + 1)
}
return <div>
<Child2 count={count}/>
<button onClick={increaseCount}>Increase</button>
<Child1/>
</div>
}
ReactDOM.render(
<SillyContext.Provider value={{name: 'test'}}>
<App />
</SillyContext.Provider>,
document.getElementById('root')
);
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<div id="root"></div>
Upvotes: 0