Company logo
  • Empleos
  • Bootcamp
  • Acerca de nosotros
  • Para profesionales
    • Inicio
    • Empleos
    • Cursos y retos
    • Preguntas
    • Profesores
    • Bootcamp
  • Para empresas
    • Inicio
    • Nuestro proceso
    • Planes
    • Pruebas
    • Nómina
    • Blog
    • Calculadora

0

73
Vistas
React functional components in Array.map are always rerendering when getting passed a function as props

I am trying to render multiple buttons in a parent component that manages all child states centrally. This means that the parent stores e.g. the click state, the disabled state for each button in his own state using useState and passes it as props to the childs. Additionally the onClick function is also defined inside of the parent component and is passed down to each child. At the moment I am doing this like the following:

const [isSelected, setIsSelected] = useState(Array(49).fill(false));
...
const onClick = useCallback((index) => {
     const newIsSelected = [...prev];
     newIsSelected[i] = !newIsSelected[i];
     return newIsSelected;
}, []);
...

(In the render function:)

return isSelected.map((isFieldSelected, key) => {
     <React.Fragment key={key}>
          <TheChildComponent
               isSelected={isFieldSelected}
               onClick={onClick}
          />
     </React.Fragment/>
})

To try to prevent the child component from rerendering I am using...

  • ... useCallback to make react see that the onClick function always stays the same
  • ... React.Fragment to make react find a component again because otherwise a child would not have a unique id or sth similar

The child component is exported as:

export default React.memo(TheChildComponent, compareEquality) with

 const compareEquality = (prev, next) => {
      console.log(prev, next);
      return prev.isSelected === next.isSelected;
 }

Somehow the log line in compareEquality is never executed and therefore I know that compareEquality is never executed. I don't know why this is happening either.

I have checked all blogs, previous Stackoverflow questions etc. but could not yet find a way to prevent the child components from being rerendered every time that at least one component executes the onClick function and by doing that updated the isSelected state.

I would be very happy if someone could point me in the right direction or explain where my problem is coming from.

Thanks in advance!

5 months ago · Juan Pablo Isaza
2 Respuestas
Responde la pregunta

0

This code will actually generate a new onClick function every render, because useCallback isn't given a deps array:

const onClick = useCallback((index) => {
     const newIsSelected = [...prev];
     newIsSelected[i] = !newIsSelected[i];
     return newIsSelected;
});

The following should only create one onClick function and re-use it throughout all renders:

const onClick = useCallback((index) => {
     const newIsSelected = [...prev];
     newIsSelected[i] = !newIsSelected[i];
     return newIsSelected;
}, []);

Combined with vanilla React.memo, this should then prevent the children from re-rendering except when isSelected changes. (Your second argument to React.memo should have also fixed this -- I'm not sure why that didn't work.)

As a side note, you can simplify this code:

     <React.Fragment key={key}>
          <TheChildComponent
               isSelected={isFieldSelected}
               onClick={onClick}
          />
     </React.Fragment/>

to the following:

          <TheChildComponent key={key}
               isSelected={isFieldSelected}
               onClick={onClick}
          />

(assuming you indeed only need a single component in the body of the map).

5 months ago · Juan Pablo Isaza Denunciar

0

Turns out the only problem was neither useCallback, useMemo or anything similar.

In the render function of the parent component I did not directly use

return isSelected.map(...)

I included that part from a seperate, very simple component like this:

const Fields = () => {

     return isSelected.map((isFieldSelected, i) => (
       <TheChildComponent
            key={i}
            isSelected={isFieldSelected}
            onClick={onClick}
       />
     ));

};

That is where my problem was. When moving the code from the seperate component Fields into the return statement of the parent component the rerendering error vanished.

Still, thanks for the help.

5 months ago · Juan Pablo Isaza Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar empleo Planes Nuestro proceso Comercial
Legal
Términos y condiciones Política de privacidad
© 2023 PeakU Inc. All Rights Reserved.