React JS Freezes Browser
Asked Answered
S

2

9

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.

enter image description here

Shangrila answered 12/1, 2016 at 16:59 Comment(2)
Any reason you can't paginate them? Or use this to display your elements: facebook.github.io/fixed-data-tableRemake
do you implement shouldComponentUpdate for the 2000 children?Maier
L
7

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.

enter image description here

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.

  1. 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 reusable useUpdateEffect 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.

  1. 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:

  1. 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.

  1. 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.

  1. 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 in reducerB. When you call to update reducerA in reducerB, the update in reducerA would trigger an update in reducerB 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 />
}
Luncheon answered 28/4, 2020 at 23:31 Comment(2)
Thanks a lot man! My case was the number 2 of your list. I was using random keys and for some reason the app started to loop sometimes when I filtered my list. You saved my dayVlada
Nice answer but please fix the typo: conso -> consoleCerveny
D
2

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.

Dkl answered 13/1, 2016 at 0:3 Comment(2)
Thanks everyone. Because of my heavy DOM size, react gets choked with diffing. I have used a nice component react-list (github.com/orgsync/react-list), saved my day massively. Amazing compnent (y)...Shangrila
@Shangrila what are you returning from the 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.