And how we stopped our React Context re-rendering everything Refs are a seldom-used feature in React. If you’ve read the , they’re introduced as an “escape hatch” out of the typical React data flow, with a warning to use them sparingly, and they’re primarily billed as the correct way to access a component’s underlying DOM element. official React guide But alongside the concept of Hooks, the React team introduced the Hook, which extends this functionality: useRef is useful for more than the attribute. It’s similar to how you’d use instance fields in classes. useRef() ref handy for keeping any mutable value around While I overlooked this point when the new Hook APIs launched, it proved to be surprisingly useful. 👉 Click here to skip to the solution and code snippets The Problem I’m a software engineer working on , an open-source React app that combines a spreadsheet UI with the full power of Firestore and Firebase. One of its key features is the , a form-like UI to edit a single row, that slides over the main table. Rowy side drawer When the user clicks on a cell in the table, the side drawer can be opened to edit that cell’s corresponding row. In other words, what we render in the side drawer is dependent on the currently selected row — this should be stored in state. The most logical place to put this state is within the side drawer component itself because when the user selects a different cell, it should affect the side drawer. However: only We need to this state from the table component. We’re using to render the table itself, and it accepts a callback prop that’s called whenever the user selects a cell. Currently, it’s the only way to respond to that event. set react-data-grid But the side drawer and table components are siblings, so they can’t directly access each other’s state. React’s recommendation is to to the components’ closest common ancestor, in this case, . But we decided against moving the state here because: lift this state TablePage didn’t contain any state and was primarily a container for the table and side drawer components, neither of which received any props. We preferred to keep it this way. TablePage We were already sharing a lot of “global” data via a located close to the root of the component tree, and we felt it made sense to add this state to that central data store. context Side note: even if we put the state in TablePage , we would have run into the same problem below anyway. The problem was whenever the user selected a cell or opened the side drawer, the update to this global context would cause . This included the main table component, which could have dozens of cells displayed at a time, each with its own editor component. This would result in a render time of around (!), long enough to see a visible delay in the side drawer’s open animation. the entire app to re-render 650 ms Notice the delay between clicking the open button and when the side drawer animates to open The reason behind this is a key feature of context — the very reason why it’s better to use in React as opposed to global JavaScript variables: All consumers that are descendants of a Provider will re-render whenever the Provider’s prop changes. value While this Hook into React’s state and lifecycle has served us well so far, it seems we had now shot ourselves in the foot. The Aha Moment We first explored a few different solutions (from on the issue) before settling on : Dan Abramov’s post useRef The table would still need to consume the new context, which still updates when the side drawer opens, unnecessarily. Split the context, i.e. create a new SideDrawerContext . causing the table to re-render The table would still need to call to access the side drawer’s state and . Wrap the table component in React.memo or useMemo . useContext neither API prevents it from causing re-renders This would have introduced more verbosity to our code. We also found it prevented re-renders, requiring us to spend more time fixing or restructuring our code entirely, solely to implement the side drawer. Memoize the react-data-grid component used to render the table. necessary While reading through the Hook APIs and a few more times, I finally came across that point about : useMemo useRef is useful for more than the attribute. It’s similar to how you’d use instance fields in classes. useRef() ref handy for keeping any mutable value around And more importantly: notify you when its content changes. Mutating the property . useRef doesn’t .current doesn’t cause a re-render And that’s when it hit me: We didn’t need to store the side drawer’s state — we only needed a reference to the function that sets that state. The Solution Keep the open and cell states in the side drawer. Create a ref to those states and store it in the context. Call the set state functions (inside the side drawer) using the ref from the table when the user clicks on a cell. The code below is an abbreviated version of the code used on Rowy and includes the TypeScript types for the ref: Side note: since function components run the entire function body on re-render, whenever the cell or open state updates (and causes a re-render), sideDrawerRef always has the latest value in .current . This solution proved to be the best since: The current cell and open states are stored inside the side drawer component itself, the most logical place to put it. The table component has access to its sibling’s state it needs it. when When either the current cell or open states are updated, it only triggers a re-render for the side drawer component and not any other component throughout the app. You can see how this is used in Rowy and . here here When to useRef This doesn’t mean you should go ahead and use this pattern for everything you build, though. It’s best used for when you . React’s core concepts of lifting state up and one-way data flow are enough to cover most app architectures anyway. need to access or update another component’s state at specific times, but your component doesn’t depend or render based on that state Thanks for reading! You can and follow me on Twitter . find out more about Rowy here @nots_dney Also published at https://medium.com/better-programming/how-to-useref-to-fix-react-performance-issues-4d92a8120c09