2

I have an Icon component that draw an icon and which is blinking because the parent is making it rerender for nothing. I don't understand why this is happening and how to prevent this.

Here is a snack that shows the issue.

We emulate the parent changes with a setInterval.

We emulate the icon rerendering by logging 'rerender' in the console.

Here is the code:

import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';


// or any pure javascript modules available in npm
let interval = null

const Child = ({name}) => {
  //Why would this child still rerender, and how to prevent it?
  console.log('rerender')
  return <Text>{name}</Text>
}
const ChildContainer = ({name}) => {
  const Memo = React.memo(Child, () => true)
  return <Memo name={name}/>
}

export default function App() {
  const [state, setState] = React.useState(0)
  const name = 'constant'
  // Change the state every second
  React.useEffect(() => {
    interval = setInterval(() => setState(s => s+1), 1000)
    return () => clearInterval(interval)
  }, [])
  return (
    <View>
      <ChildContainer name={name} />
    </View>
  );
}

If you could explain me why this is happening and what is the proper way to fix it, that would be awesome!

2
  • Are there some changes happening in your parent component continously? Commented May 6, 2020 at 19:39
  • We change the state onMouseEnter and onMouseLeave. This makes the icon blink Commented May 6, 2020 at 19:50

1 Answer 1

6

If you move const Memo = React.memo(Child, () => true) outside the ChildContainer your code will work as expected.

While ChildContainer is not a memoized component, it will be re-rendered and create a memoized Child component on every parent re-render.

By moving the memoization outside of the ChildContainer, you safely memoize your component Child once, and no matter how many times ChildContainer will be called, Child will only run one time.

Here is a working demo. I also added a log on the App to track every re-render, and one log to the ChildComponent so you can see that this function is called on every re-render without actually touching Child anymore.

You could also wrap Child with React.memo directly:

import * as React from "react";
import { Text, View, StyleSheet } from "react-native";

// or any pure javascript modules available in npm
let interval = null;

const Child = React.memo(({ name }) => {
  //Why would this child still rerender, and how to prevent it?
  console.log("memoized component rerender");
  return <Text>{name}</Text>;
}, () => true);

const ChildContainer = ({ name }) => {
  console.log("ChildContainer component rerender");
  return <Child name={name} />;
};

export default function App() {
  const [state, setState] = React.useState(0);
  const name = "constant";
  // Change the state every second
  React.useEffect(() => {
    interval = setInterval(() => setState(s => s + 1), 1000);
    return () => clearInterval(interval);
  }, []);

  console.log("App rerender");
  return (
    <View>
      <ChildContainer name={name} />
    </View>
  );
}

Sign up to request clarification or add additional context in comments.

3 Comments

Ok. Thanks a lot, this answers perfectly the original question. I have a new one because it still doesn't solve my problem in my case, because this breaks when using an HOC in the Parent. I can't explain why. I'll link the question here.
Could you take a look at this? It would be really helpful. stackoverflow.com/questions/61645789/…
@Sharcoux More than happy to help! I've also answered your new question. Let me know if it's not clear enough to see if I can work on a more detailed answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.