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