Red Baron
Red Baron

Reputation: 7672

How to reduce the number of times useEffect is called?

Google's lighthouse tool gave my app an appalling performance score so I've been doing some investigating. I have a component called Home

inside Home I have useEffect (only one) that looks like this

useEffect(() => {
    console.log('rendering in here?') // called 14 times...what?!
    console.log(user.data, 'uvv') // called 13 times...again, What the heck?
}, [user.data])

I know that you put the second argument of , [] to make sure useEffect is only called once the data changes but this is the main part I don't get. when I console log user.data the first 4 console logs are empty arrays. the next 9 are arrays of length 9. so in my head, it should only have called it twice? once for [] and once for [].length(9) so what on earth is going on?

I seriously need to reduce it as it must be killing my performance. let me know if there's anything else I can do to dramatically reduce these calls

this is how I get user.data

const Home = ({ ui, user }) => { // I pass it in here as a prop

const mapState = ({ user }) => ({
    user,
})

and then my component is connected so I just pass it in here

Upvotes: 2

Views: 968

Answers (2)

HMR
HMR

Reputation: 39320

This is not an answer but there is too much code to fit in a comment. First you can log all actions that change user.data by replacing original root reducer temporarlily:

let lastData = {};
const logRootReducer = (state, action) => {
  const newState = rootReducer(state, action);
  if (newState.user.data !== lastData) {
    console.log(
      'action changed data:',
      action,
      newState.user.data,
      lastData
    );
    lastData = newState.user.data;
  }
  return newState;
};

Another thing causing user.data to keep changing is when you do something like this in the reducer:

if (action.type === SOME_TYPE) {
  return {
    ...state,
    user: {
      ...state.user,
      //here data is set to a new array every time
      data: [],
    },
  };
}

Instead you can do something like this:

const EMPTY_DATA = [];
//... other code
data: EMPTY_DATA,

Your selector is getting user out of state and creating a new object that would cause the component to re render but the dependency of the effect is user.data so the effect will only run if data actually changed.

Redux devtools also show differences in the wrong way, if you mutate something in state the devtools will show them as changes but React won't see them as changes. When you assign a new object to something data:[] then redux won't show them as changes but React will see it as a change.

Upvotes: 1

Vicky
Vicky

Reputation: 642

To overcome this scenario, React Hooks also provides functionality called useMemo. You can use useMemo instead useEffect because useMemo cache the instance it renders and whenever it hit for render, it first check into cache to whether any related instance has been available for given deps.. If so, then rather than run entire function it will simply return it from cache.

Upvotes: 1

Related Questions