I have a React component which has 2000 elements and based on some filter conditions I update my state, which internally causes re-rendering. Everything seems to be working fine. But when I togglefilter from 2000 elements to say 1000 elements and back&forth, the rendering takes a lot of time and sometimes the browser freezes. I did chrome timeline profiling, the major time consuming piece is rendering. Any help would be appreciated.
As suggested by @enjoylife is a great step but what if you have many components structures in your view, that would be very difficult to debug even memoising the component won't be able to subside the continuous or loop rendering.
I learnt this after I ran into strange freezing and weird error that wouldn't stop any time a user logged in on the homepage. Imagine of all screens. Sometimes, you would hardly notice your component re-rending.
Detect your screen/page (loop) re-rendering with console log
const Home = () => {
console.log('home re-rending')
// some hooks
return <BigComponent />
}
As written above. The logs must not show more than a limited time deemed after a component has mounted. In my case, it's once. But if it is too much(logs) and would certainly freeze your pc. Therefore, follow the below steps carefully and retrace your steps.
Tips and prerequisite before trying out this proposed solution. Please make sure you have style guide setup e.g. Eslint, it's great. In my case, I reproduced the source code with cra
, then sorted out the first and last listed problem which I encountered.
- Be careful with the use of React hooks such as
useEffect
especially. Avoid causing a side effect in a component. In my case, I created a reusableuseUpdateEffect hook
and what I intend it to solve as par the name was to detect an update of React props or window props, but it backfires, I won't share the code.
Also, do extra check if you passed correct and expected dependencies, on this Eslint deserve an accolade.
- Avoid random keys in React list. Use unique and constant keys in a component list as react depend on it to identify each item. According to react library
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity. You may use the item index as a key as a last resort:
- Avoid variable name conflict in your reducer and React component. Please consider the use of style guides as your friend to avoid this fall.
I made the stupid mistake to create a Foo class and use in its render function, which also leads to the freezing scene. Write here for anyone who could meet this problem again.follow this thread.
- Avoid infinite loops, Imagine rendering a lot of data at a go. this happen
just in case you share my fate, I urge you to check your loops and make sure you do not have a += instead of -= (or vice versa). Those infinite loops can be quite a big pain in the neck.
- Keep your reducer as a reducer, Avoid Action creator, an API call in your reducer or
using another reducer in your reducer
so, for instance,reducerA
inreducerB
. When you call to updatereducerA
inreducerB
, the update inreducerA
would trigger an update inreducerB
whereby cause page/screen to re-render multiple times. for example
// this react reducer in my case
// reducer js file - reducerB
const useBusinesses = () => {
// reducerB as discussed above - the loading context
const { loading } = useLoadingContext(); // the culprit
const [data, setData] = useState(initialState); // initial state,
const [state, dispatch] = useReducer(reducer, data);
useEffect(() => setData(state), [state, setData]);
const { businesses, errorMessage } = state;
const setBusinesses = (payload) => dispatch({ type: `${FETCH_BUSINESSES}_SUCCESS`, data: payload });
const setBusinessesError = (payload) => dispatch({ type: `${FETCH_BUSINESSES}_ERROR`, data: payload });
const fetchBusinesses = async (lglt, type = 'food', limit = 12) => {
try {
// update reducerB: triggers multiple update in reducerA while requesting is pending
loading(FETCH_BUSINESSES, true);
const request = await API.businesses.getWithquery(
`long=${lglt[0]}&latt=${lglt[1]}&limit=${limit}&type=${type}`
);
loading(FETCH_BUSINESSES, false);
setBusinesses(request.data);
} catch (err) {
loading(FETCH_BUSINESSES, false);
// if (!err.response) dispatch(alertMessage(FETCH_BUKKAS, true, 'Please check your network'));
setBusinessesError(err.response.data);
}
});
return { businesses, errorMessage, fetchBusinesses };
};
export const [BusinessesProvider, useBusinessesContext] = constate(useBusinesses);
//home js file
Home = () => {
const { fetchBusinesses } = useBusinessContext();
console.log('home re-rending')
// some hooks
useEffect(() => {
console.log('am i in trouble, yes!, how many troubles')
fetchBusinesses(coordinates)
}, [fetchBusinesses, coordinates])
return <BigComponent />
}
A quick fix is to implement shouldComponentUpdate
See the docs, for whichever child component is being rendered ~2000 times.
shouldComponentUpdate: function(nextProps, nextState) {
return this.props.value !== nextProps.value;
}
Another quick check is to ask yourself if your following the convention of using small, stateless children, passing only props. If not, it might be time to refactor.
itemRenderer
method? Can you return a React Class or are you returning React Elements. I can't get the react-list
module to work with my variable list of elements. It freezes the browser when scrolling around. Any idea? –
Provincialism © 2022 - 2024 — McMap. All rights reserved.
shouldComponentUpdate
for the 2000 children? – Maier