Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

169
Vistas
useState value changes when i call it from child

I'm creating a timer app built using react-hooks and an array of this timers I don't understand why timerList changes

Here it is the parent component

const [timerList, setTimerList] = useState([]);


const removeTimer = () => {
  console.log("timerList", timerList);
};

return (
    <div id="main">
      {timerList ? timerList.map((child) => child) : null}
      <div className="add-button before">
        <button
          onClick={() => {
            const time = new Date();
            time.setSeconds(time.getSeconds() + 0);
            setTimerList((timerList) => [
              ...timerList,
              <FullTimer
                expiryTimestamp={time}
                removeTimer={() => {
                  removeTimer();
                }}
                id={window.prompt("Insert timer name") + ` ${timerList.length}`}
                key={timerList.length}
              />,
            ]);
          }}
        >

The interested child's component part:

<button
  onClick={() => {
     removeTimer();
  }}
>

The child component is a custom timer with some css, and when i call removeTimer the value of timerList (in the parent component) changes, when it should remain the same.

What am I missing?

P.S. the button tags aren't closed because i have some element inside them that use awesome-font

about 4 years ago · Juan Pablo Isaza
3 Respuestas
Responde la pregunta

0

Side note: In general it's considered bad practice to store components in another components state.


But that's not really the problem here. Given your code, it's a simple closure problem.

This:

const removeTimer = () => {
  console.log("timerList", timerList);
};

definition closes over the current timerList. So it will log it, as it was when removeTimer was assigned. Currently that's on every render. So it will log the state seemingly one step behind. There's no fix around that, because that's just how closures work.

Provided you actually want to remove a timer, when removeTimer is invoked, you would need to use the callback version of the updater (setTimerList) and pass some identifying value so that you can actually remove the correct one.

This would all be a lot simpler, if you followed the initial advice and don't store the component in the state, but rather it's defining properties.


The following would be a working example (please excuse my typescript):

import React, { useState } from 'react';

type FullTimerProps = {
  id: string;
  expiryTimestamp: Date;
  removeTimer: () => void;
}

const FullTimer = ({expiryTimestamp, removeTimer, id}: FullTimerProps): JSX.Element => {
  return (
    <div>
      <button onClick={removeTimer}>remove</button>
      {id}: {expiryTimestamp.toLocaleDateString()}
    </div>
  );
};

type Timer = {
  id: string;
  expiryTimestamp: Date;
};

const TimerList = (): JSX.Element => {
  const [timerList, setTimerList] = useState<Timer[]>([]);

  const removeTimer = (timer: Timer) => {
    setTimerList(timerList => timerList.filter(t => t.id !== timer.id));
  };

  return (
    <div id="main">
      {timerList.map(timer => (
        <FullTimer
          key={timer.id}
          id={timer.id}
          expiryTimestamp={timer.expiryTimestamp}
          removeTimer={() => removeTimer(timer)}
        />
      ))}

      <div className="add-button before">
        <button
          onClick={() =>
            setTimerList(timerList => [...timerList, {
              id: window.prompt('Insert timer name') + ` ${timerList.length}`,
              expiryTimestamp: new Date()
            }])}
        >Add
        </button>
      </div>
    </div>
  );
};
about 4 years ago · Juan Pablo Isaza Denunciar

0

changing this code snippet

            setTimerList((timerList) => [
              ...timerList,
              <FullTimer
                expiryTimestamp={time}
                removeTimer={() => removeTimer()}
                id={window.prompt("Insert timer name") + ` ${timerList.length}`}
                key={timerList.length}
              />,
            ]);

to

            timerList.push(<FullTimer
              expiryTimestamp={time}
              removeTimer={() => removeTimer()}
              id={window.prompt("Insert timer name") + ` ${timerList.length}`}
              key={timerList.length}
            />);
            setTimerList([...timerList]);

Fixed the problem you are having. Although this change is not recommended because it is not immutable approach, but it fixes this case.

UPDATE: It turned out that you duplicated the removeTimer function during the setTimerList call which cause the child component to capture the timerList at the moment of assignment. Which is mentioned at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures?retiredLocale=vi#closure as mr @yoshi has shown

about 4 years ago · Juan Pablo Isaza Denunciar

0

Try to write your onclick function like this

<button
  onClick={() => removeTimer()}
>

Also over here

 <FullTimer
    expiryTimestamp={time}
    removeTimer={() => removeTimer()}
about 4 years 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 vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda