Skip to content
Advertisement

how can I prevent extra background operations when I change an state in react?

Introducing Code

I attempted to write an Exercising code by React which illustrates a bus and its seats. The seats that are reserved, are unselectable and red. The rest of the seats are selectable by clicking on them and choosing a gender at an opened popup. You can see and try it here.

enter image description here

The Problem

Everything works well. But my problem is about performance. I know that when a State changes, that component will be rendered again. Of course, only changes will be passed to DOM. It’s OK.

The problem starts here that in the background, the component codes and its children’s codes are executed completely. Consider my bus has 24 seats and a seat is a component. Every time you click on seats, whether you choose a gender or not, you will see many logs in the console which means (at least I think it means) everything is executed again.

My Request

how can I prevent extra background operations And improve the structure to avoid them?

I have no particular insistence to use usestate if I can solve it in some way.

Advertisement

Answer

This is the intended behavior on a React component, if the state re-renders, all the children re-renders if there is a change. In your case, you are passing OnSelectSeatHandler as a prop to the BusSeat component, which is a function. Since javascript evaluates () => {} === () => {} to false, your react component will behave as if the prop changed on each render. So, the flow is: seat clicked -> component rendered -> children rendered.

One Solution

We need to tell React that the BusSeat shouldn’t re-render when BusBody re-renders. To achieve this we need two things: memo and useCallback from “react”. These do not come without a tradeoff, there are plenty of good resources to read on learning when to use memo, useMemo and useCallback.

That being said, what I did is:

  1. Wrap the BusSeat component to memo:
  import { memo } from "react";

  export default memo(function BusSeat(props) {
  1. Wrap the OnSelectHandler to useCallback. For this, we need a clean dependency array so that function doesn’t actually get re-rendered on each button click. Therefore, I removed the seatClicked and moved GetItem to the top of the react component so it doesn’t get recreated:
 const OnSelectSeatHandler = useCallback(
   (e, number) => {
     var seat = GetItem(number, props.items);
     if (seat[0].IsEmpty) {
       setSeatClicked((prevSeat) => (prevSeat === null ? number : null));
       setXPos(e.target.offsetLeft);
       setYPos(e.target.offsetTop);
     } else if (seat[0].user) {
       props.items[seat[1]] = {
         ...seat[0],
         IsEmpty: true,
         user: false,
         type: ""
       };
       setSelectedSeatCount((prev) => prev - 1);
     }
   },
   [props.items]
 );

Here is a working codesandbox

Note: Try to avoid updating props.items as you do in props.items[1] = ...

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement