Will a React Component Re-Render If Its Props Are Updated, But Don't Change in Value

React component re-rendering although all props do not change

This is my mistake. I use a same function and dont push response to store and solve this problem.

useEffect(() => {
const fetchData = async () => {
const username = await AsyncStorage.getItem('username');
const tokenKey = await AsyncStorage.getItem('tokenKey');

try {
const params = {
method: 'GET',
headers: {
Authorization: `Bearer ${tokenKey}`,
},
};

const response = await fetch(
`${API_GET_TI_BASE}?loginName=${username}`,
params,
);
const body = await response.json();

setlocalData(body);
} catch (error) {
console.log('getCarInfo error: ', error);
}
};

const interval = setInterval(() => {
fetchData();
// onGetCarInfo();
}, 15000);

return () => {
clearInterval(interval);
};
}, []);

Why is React component rerendering when props has not changed?

As commented; when mapStateToProps returns a new object it will re render the connected component even if no relevant values change.

This is because {} !== {}, an object with same props and values does not equal another object with same props and values because React compares object reference and not the values of the object. That is why you can't change state by mutating it. Mutating changes the values in the object but not the reference to the object.

Your mapStateToProps has to return a new reference at the 2nd level for it to re render with the same values, so {val:1} won't re render but {something:{val:1}} will.

The code below shows how not memoizing the result of mapStateToProps can cause re renders:

const { Provider, connect, useDispatch } = ReactRedux;const { createStore } = Redux;const { createSelector } = Reselect;const { useRef, useEffect, memo } = React;
const state = { val: 1 };//returning a new state every action but no values// have been changedconst reducer = () => ({ ...state });const store = createStore( reducer, { ...state }, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());const Component = (props) => { const rendered = useRef(0); rendered.current++; return ( <div> <div>rendered:{rendered.current} times</div> props:<pre>{JSON.stringify(props)}</pre> </div> );};const selectVal = (state) => state.val;const selectMapStateToProps = createSelector( selectVal, //will only re create this object when val changes (val) => console.log('val changed') || { mem: { val } });const memoizedMapStateToProps = selectMapStateToProps;const mapStateToProps = ({ val }) => ({ nonMem: { val } }); //re creates props.nonMem every timeconst MemoizedConnected = connect(memoizedMapStateToProps)( Component);//this mapStateToProps will create a props of {val:1}// pure components (returned by connect) will compare each property// of the prop object and not the props as a whole. Since props.val// never changed between renders it won't re renderconst OneLevelConnect = connect(({ val }) => ({ val }))( Component);const Connected = connect(mapStateToProps)(Component);const Pure = memo(function Pure() { //props never change so this will only be rendered once console.log('props never change so wont re render Pure'); return ( <div> <Connected /> <MemoizedConnected /> <OneLevelConnect /> </div> );});const App = () => { const dispatch = useDispatch(); useEffect( //dispatch an action every second, this will create a new // state ref but state.val never changes () => { setInterval(() => dispatch({ type: 88 }), 1000); }, [dispatch] //dispatch never changes but linting tools don't know that ); return <Pure />;};
ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/4.0.0/reselect.min.js"></script>

<div id="root"></div>

Will a stateless component re-render if its props have not changed?

UPDATE 25.10.2018

Since React 16.6, you can use React.memo for functional components to prevent re-render, similarly to PureComponent for class components:

const MyComponent = React.memo((props) => {
return (
/* markup */
);
});

Also, memo does internal optimization.

And unlike a userland memo() higher-order component implementation, the one built into React can be more efficient by avoiding an extra component layer.
Blockquote


OLD ANSWER

Yes, they always re-render 1 (unless you use React.memo as explained above) if setState() is called in the component itself or one of its parents, because functional stateless components don't carry a shouldComponentUpdate. In fact, each React component is being re-rendered1 unless they implement shouldComponentUpdate.


Important to note is that calling render() doesn't mean that DOM Nodes are being manipulated in any way. The render method just serves the diff algorithm to decide which DOM Nodes need to really be attached / detached. Note that render() is not expensive, it's the DOM manipulations that are expensive. They are executed only if render() returns different virtual trees.

From React's documentation

Just to be clear, rerender in this context means calling render for all components, it doesn’t mean React will unmount and remount them. It will only apply the differences following the rules stated in the previous sections.

Just don't worry and let render() be called unless your component is huge, then you're better off with stateful Component that implements shouldComponentUpdate().

Look here for an interesting discussion.

1 means that render() function of the component is called, not that the underlying DOM node is being manipulated.

React 16.13.1 - child component does not re-render when props change

It looks like the same expression object is being passed in all the time.

React checks the props that a component receives for changes when deciding to render. It finds that none of the props items have changed, they are all the same objects as before, and concludes that the child component does not need to be rerendered. It will not do a deep inspection of all properties of each prop.

This also explains why a rerender can be forced by making a copy of the expression object. The copy is always a new object, thus causing a rerender, regardless if any of its content have changed or not.

You could avoid this situation as you do already, by making a copy, or by dissecting the expression object into its properties and then feeding each of those as separate props into the child.

As a final note, a copy can also be made by passing it in as expression={{...expression}}.

How to avoid unnecessary re-render of a component in React?

React useState state updater functions are guaranteed to be stable references, so Child2 is only rerendering because the parent component Parent is rerendering due to the state update. If you want to hint to React that it can likely bail on rerendering a child component then use the memo Higher Order Component.

const Child2 = ({ setValue }) => {
....
};

export default memo(Child2);

Demo

Demonstrates 2 "instances" of Child 2, one decorated with the memo HOC and the other not. All children use an useEffect to log when they are rendered. Note that all initially render, but only child 1 and 2 (w/o memo) continually are rerendered.

Edit how-to-avoid-unnecessary-re-render-of-a-component-in-react

Sandbox code:

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

const Child1 = ({ value }) => {
useEffect(() => console.log("Child 1 rendered"));
return <div>Child1: {value}</div>;
};

const Child2 = ({ id, setValue }) => {
useEffect(() => console.log(`${id} rendered`));
return (
<div>
{id}: <input type="text" onChange={(e) => setValue(e.target.value)} />
</div>
);
};

const Child2wMemo = memo(Child2);

const Parent = () => {
const [value, setValue] = useState("");

return (
<>
<Child1 value={value} />
<Child2 id="Child 2" setValue={setValue} />
<Child2wMemo id="Child 2 with Memo" setValue={setValue} />
</>
);
};

export default function App() {
return (
<div className="App">
<Parent />
</div>
);
}

Sample Image



Related Topics



Leave a reply



Submit