Skip to content

react

5 posts with the tag ā€œreactā€

When you need re-render, but it doesn't happen...

I already have had this issue and was surprised by this behaviour, but I forgot this ā€” and wasted 2 hours on debugging. I suspected componentā€™s library, checked contexts, and tried to find problems in composition.

So, what happened? In our App.tsx:

<Router history={history}>
<UuiContext.Provider value={services}>
<Header />
<Routes />
<Footer />
</UuiContext.Provider>
</Router>

In Header.tsx ā€” navigation:

// it's component from library, responsible for highlighting active menu item
// use uuiContext from UuiContext.Provider and check is link active using history
<MainMenu>
<MainMenuButton caption='Home' link={{pathname: '/' } />
<MainMenuButton caption='Dashboard' link={{pathname: '/dashboard' } />
</MainMenu>

And it worksā€¦ almost. When you click on buttons in MainMenu url is updated, content is re-rendered. But there is no highlighting for active menu item in MainMenu.

Started with MainMenu.tsx: add breakpoint in function defining is link active. But on button clicks and changing url this function is not called. And component is not re-rendered.

Add console.log(ā€˜renderā€™) in Header.tsx. So, it is also not re-rendered. Despite updating the history object, and seeing those updates reflected in uuiContext.uuiRouter.history, nothing was triggering a re-render. Why donā€™t children of UuiContext.Provider update?

At this point, I went down a rabbit hole: tried to debug in contexts, because I thought that I have several instances and something weird happens. Found nothing, of course.

But after all this debugging, idea occurred to me: content is re-rendered because of changing actual Route, what if I put Header in corresponded pages? It worked, but in this case Header unmounted and mounted each time route changes. Itā€™s not good user experience.

And then finally I asked right thing: how React and React Router define, what should be re-rendered? Results of answering on this question:

  • we update history
  • history object is stable reference, so you cannot track changes with useEffect and history.location dependency
  • but useLocation and useParams watch changes location, and when we use them ā€” our component re-renders after changes

So I just added to Header.tsx:

useLocation();
//...same code

The most annoying thing about that I already have solved problem like this, but remembered about it only when I found solution again. Writing it down this time so Iā€™ll remember šŸ¤Ŗ

The big re-renders myth

Have you ever heard that writing code like this:

<Component levels={[1, 2, 3]} />

is bad practice because the levels array is recreated in JSX every time, causing unnecessary re-renders of Component? And that you should use useMemo (like const levels = useMemo(() => [1, 2, 3], [])) or declare it outside the component?

Iā€™ve heard this advice many times. And I followed it.

Even when I encountered cases where passing a ref object as a prop and then updating the ref didnā€™t trigger a re-render, I didnā€™t question it. I just hadnā€™t connected the dots ā€” if a component always re-renders when its props change, then it should work the same way with ref, right?

Now, while reading Advanced React by Nadia Makarevich, I came across one of the most common misconceptions in React:

The big re-renders myth: Component re-renders when its props change.

React updates when a state update is triggered. In that case, it will re-render all nested components, regardless of whether their props have changed.

In the context of re-renders, whether props have changed or not on a component matters only in one case: if the said component is wrapped in the React.memo higher-order component.

Check this out with code: https://codesandbox.io/p/sandbox/props-rerender-dcq97c.

Thatā€™s it! We need to shift our thinking paradigm ā€” from focusing on prop changes to analyzing state updates.

P.S. For constant arrays/objects, I personally prefer defining them in a separate constants.ts file. But thatā€™s a matter of taste, not re-renders.

HOC or not HOC?

In a set of katas for exploring patterns, thereā€™s one about HOCs: truncate paragraph with HOC in React. Recently, Iā€™ve also been diving into the Decorator Pattern, and in React, an HOC could be considered an example of a Decorator.

But is the HOC still a thing?

I often come across HOCs in codebases that are 3ā€“4 years old (or older),but I rarely see them in new projects. In the old React documentation, there was even a dedicated page about HOCs, complete with examples. But in the shiny new React documentation, HOCs are nowhere to be found.

It seems like custom hooks have taken over this territory and mostly replaced HOCs. What can you do with an HOC that you canā€™t achieve with a custom hook?

Personally, I prefer custom hooksā€”or even classes with their own logic. It is more obvious and clear for me.

Today I learned about ref callbacks

It escaped my attention, we can pass a callback function as a ref for an element in React, not just an object (like from useRef).

const scroller = (node: HTMLDivElement) => {
if (!node) return;
node.scrollIntoView({ behavior: "smooth" });
};
// somewhere in component
<div ref={scroller} />

How it works:

  • when the element is added to the DOM, React calls the ref callback with the DOM node as its argument
  • when the element is removed from the DOM, React calls the ref callback with null
  • also the callback is invoked whenever a new callback is assigned (on each render if it is written as a simple function)

Watch out for divs

I want to tell about a problem I solved today at work.

We have a table with filters that display a list of offices visits. Some visits require additional attention from managers, such as when an access card is issued for a visit, but the visit ends and the card hasnā€™t been returned.

The task was to highlight such visits in the table. I did this by highlighting the row and adding a banner at the top. This banner allows for quickly filtering visits that require attention.

I implemented this (including extra functionality to avoid saving the filter with only highlighted visits), and it worked ā€” until some issues appeared with the filters.

Most of the filters are dropdowns with a list of options. On this feature branch, the dropdowns became slow, and after 3-4 fast clicks, the page started to freeze. The console showed errors about ā€œMaximum update depth exceededā€ and recommended checking my useEffects. The stack trace pointed to the dropdown component from our companyā€™s component library.

What I Did To Solve The Problem

I started by trying to understand what was happening. Additional difficulty was that ghe issue appeared after I added code from another package in our monorepo (I use npm workspaces), and there were a lot of changes in the code.

Debugging was challenging, as the dropdown component seemed fine, and I couldnā€™t see any changes that could have caused the issue.

I tried Googling and asking Claude (AI assistance), but then turned to one of my favorite debugging methods ā€” removing parts of the code.

First, I wanted to see if the issue came from using the new package. I created a new branch from our develop branch and moved minimal feature changes there. The problem was still present.

Next, I removed parts of the feature code, including requests and data handling. The problem persisted.

I was almost ready to give up, but I decided to remove all the feature code. And suddenly, the problem disappeared.

Finally, I found the issue: I had added a div to wrap the table and the banner above it. When I replaced it with a fragment, the issue disappeared.