Hey there! 👋
I need some help!
I'm adding a small feature to our React social app, a floating action button (FAB) to create new posts
Here's the original code:
const App = () => {
console.log('other code here')
return (
<div className="main">
<Posts config={configs}/>
<Settings />
<AdServe id={adId}/>
</div>
);
};
And this is my approach:
const App = () => {
const [isModalOpen, setModalOpen] = useState(false);
console.log('other code here')
return (
<div className="main">
<Posts
config={configs}
data={posts}
/>
<Settings />
<AdServe id={adId}/>
<FAB
onClick={() => setModalOpen(true)}
icon={"lg_plus"}
/>
{ isModalOpen &&
<CreatePostModal onCreated={handleCreatePost}/> }
</div>
);
};
The dialog opens fine, but the performance isn't great, the dialog the dialog takes a few seconds to open. Can we figure out why?
Hey! Absolutely, let's dive in.
Have you noticed that the
App
component rerenders whenever the dialog is opened or closed?Yes, I've seen that all the components within
App
re-render when I click the FABI think I understand now. The root
App
component is re-rendering, so I should use React.memo
and useCallback
to prevent this, right?That's a valid approach, but there's a simpler way. Let's consider moving the state down the component tree and utilizing component composition to address this. Let's take a look:
const App = () => {
console.log('other code here')
return (
<div className="main">
<Posts config={configs} data={posts}/>
<Settings />
<AdServe id={adId}/>
<CreatePostModalWithFAB />
</div>
);
};
In
CreatePostModalWithFAB.tsx
:const CreatePostModalWithFAB = () => {
const [isModalOpen, setModalOpen] = useState(false);
return (
<>
<FAB
onClick={() => setModalOpen(true)}
icon={"lg_plus"}
/>
{ isModalOpen &&
<Modal onCreated={handleCreatePost}>
{content}
</Modal> }
</>
);
};
I see, we've localized the modal state within the
CreatePostModal
component. It seems to have resolved the performance issue! Could you explain why this works?Absolutely, there's a bit to unpack here, and it involves going over some core concepts.
Let's look at one of the most important stages of a component that we're interested in when dealing with performance for any react app - the re-rendering phase
Any re-render is triggered from changes done to the state, be it from
useState
or any state management libraries like mobex
.Take for example our
App
componentconst App = () => {
const [isModalOpen, setModalOpen] = useState(false);
return (
<div>
<Posts />
<Settings />
<AdServe />
<FAB
onClick={() => setModalOpen(true)}
/>
{ isModalOpen && <CreatePostModal /> }
</div>
);
};
When the FAB is clicked, we use the
setModalOpen
setter to set isModalOpen
to true, after which the App
component that has the state re-rendersOnce this new state is updated, react then re-renders components like
<Posts />
, <Settings />
, <AdServe />
nested within App
until it reaches the end of the component tree.The component with the state and its nested components will be re-rendered
CreatePostModal
component, only that component will re-render when the state changes, and the App
component will be unaffected