DEV Community

Cover image for Why is my React component flickering?
Werliton Silva
Werliton Silva

Posted on

Why is my React component flickering?

Understanding Re-renders with React Context and how to avoid waste

Have you ever wondered why your component in React re-renders even without anything visibly changing? Let's understand this with simple examples – as if we were assembling a block game.

Basic scenario

Imagine three components, one inside the other:

<App>
  <Container>
    <SearchContainer>
      <Search />
    </SearchContainer>
  </Container>
</App>
Enter fullscreen mode Exit fullscreen mode

And let's say you have a Context at the top (App) that shares some data with everyone below.

context

What happens when App changes?

If <App /> re-renders (for any reason), all components below it also re-render:

App -> Container -> SearchContainer -> Search
Enter fullscreen mode Exit fullscreen mode

Even if only the last one (Search) actually uses the Context!

How to avoid unnecessary Re-renders

If Container and SearchContainer don't need to change, we can avoid the effort of re-rendering them by using:

const Container = React.memo(() => {
  return <SearchContainer />;
});
Enter fullscreen mode Exit fullscreen mode

What is React.memo?

It's like saying:

"Hey React, if nothing changes here, don't call me again!"


But what about when we use objects in Context?

Now for the trick:

const value = { a: 'tchaca', b: 'arrocha' };

<SomeContext.Provider value={value}>
  <Search />
</SomeContext.Provider>
Enter fullscreen mode Exit fullscreen mode

If App re-renders, the value object is recreated. And for React:

{ a: 'tchaca', b: 'arrocha' } !== { a: 'tchaca', b: 'arrocha' }
Enter fullscreen mode Exit fullscreen mode

Even though they look the same, they are different objects (because the memory reference changed). Because of this, React thinks the context has changed and causes Search to re-render unnecessarily.


The Solution: useMemo

Use useMemo to say:

"Only give me a new object if something truly changes."

const value = useMemo(() => {
  return { a: 'tchaca', b: 'arrocha' };
}, [a, b]); // 'a' and 'b' are dependencies. If they don't change, the object isn't recreated.
Enter fullscreen mode Exit fullscreen mode

Now, if App re-renders but the values a and b are the same, the object remains the same - and React doesn't ask Search to re-render.


Complete example:

// Context
import React, { createContext, useMemo } from 'react';

const MyContext = createContext(null);

export const MyProvider = ({ children }) => {
  const [a, setA] = React.useState('tchaca'); 
  const [b, setB] = React.useState('arrocha');

  const value = useMemo(() => ({ a, b }), [a, b]); 

  // You might have a button to change 'a' or 'b' here to see the effect
  // For demonstration, let's just show the rendering behavior

  return (
    <MyContext.Provider value={value}>
      { children }
    </MyContext.Provider>
  );

}
Enter fullscreen mode Exit fullscreen mode
// Consumer
import React, { useContext } from 'react';


const Search = () => {
  const { a } = useContext(MyContext);
  console.log('Search rendered');
  return <div>Hello: {a}</div>;
};

const SearchContainer = () => {
  return <Search />;
};

const Container = React.memo(() => {
  return <SearchContainer />;
});

export default function App() {

  return (
    <MyProvider>
      <Container />
    </MyProvider>
  );
}

Enter fullscreen mode Exit fullscreen mode

Note: In the Complete Example, I added useState for a and b in App to make the useMemo dependency array [a, b] functional in a real-world scenario. Without useState or props, a and b would just be static string literals inside App and useMemo's dependency array would essentially be empty if they weren't defined as variables outside the useMemo call.


Conclusion

  • Context is great for sharing data.
  • But it re-renders children if the value changes.
  • Even if the value looks the same, if it's a new object, React will think it's different.
  • Use React.memo to prevent unnecessary re-renders in components that don't change.
  • Use useMemo to ensure the Context value remains the same if the data hasn't changed.

Does this make sense to you?
Do you have another tip to improve performance with Context API?

Top comments (10)

Collapse
 
nevodavid profile image
Nevo David

nice breakdown, context stuff always tripped me up tbh - you ever feel like chasing every tiny perf issue is worth it long term, or sometimes you just gotta let a few go?

Collapse
 
werliton profile image
Werliton Silva

It's cool. I don't always catch all the performance problems, because the information isn't always enough. So I focus on specific issues that I can identify and solve them one by one.

Collapse
 
michael_liang_0208 profile image
Michael Liang

Great post. I really like your posts.

Collapse
 
werliton profile image
Werliton Silva

Thanks bro

Collapse
 
michael_liang_0208 profile image
Michael Liang

Please check your email. I will wait for it.
Thanks 😄

Collapse
 
werliton profile image
Werliton Silva

Okay. See you later

Collapse
 
dev_king profile image
Dev King

Very good breakdown

Collapse
 
werliton profile image
Werliton Silva

Did you see the 'tchaca'?

Collapse
 
nathan_tarbert profile image
Nathan Tarbert

been cool seeing steady progress - it adds up. you think consistency or just always looking for tiny wins matters more over time?

Collapse
 
werliton profile image
Werliton Silva

It's cool. I don't always catch all the performance problems, because the information isn't always enough. So I focus on specific tiny issues that I can identify and solve them one by one.