Значение не меняется в крючке useEffect

#reactjs #react-hooks

Вопрос:

Вот мой компонент:

 import { useEffect, useState } from "react";

function Com() {
  const [x, setX] = useState(0);
  useEffect(() => {
    //here is some code that I want to run only once.
    //edit: bcz I want to creating variable here and access in setInterval
    setInterval(() => {
      console.log(x);
      //problem is here x not updating
    }, 1000);
  }, []);
  function change() {
    console.log(x);
    // incrementing x on every click
    setX(x   1);
  }
  return <button onClick={change}>Change X</button>;
}
export default Com;

 

Я хочу обновить значение x в крючках useEffect.
Если я использую x в качестве массива зависимостей, весь код в useEffect снова запускается, чего я не хочу.

Комментарии:

1. Хорошо, можете ли вы уточнить, чего вы хотите достичь? Согласно вашему описанию, вы хотели бы «обновить значение x в useEffct крючке». Тем не менее, в вашем примере вы просто выводите значение x в крючке.

Ответ №1:

Вы можете получить доступ к старому/текущему состоянию, передав функцию setState .

 useEffect(() => {
  const interval = setInterval(() => {
    setX(oldX => /* do something about it */)
  }, 1000);

  // don't forget to do clean up
  return () => clearInterval(interval);
}, []);
 

Ответ №2:

В JavaScript переменная может хранить значения двух типов: примитивные и ссылочные.

JavaScript предоставляет шесть примитивных типов, таких как неопределенный, нулевой, логический, число, строка и символ , а также объект ссылочного типа.

Поскольку x-это число (которое является примитивом), оно передается по значению, а не по ссылке. И поскольку он передается только один раз, значение передается только один раз. Если вы хотите передать его по ссылке, вы можете обернуть x в объект.

 const [x, setX] = useState({value:0});
 

Комментарии:

1. Я не думаю, что это сработает. Согласно вашему предложению, каждый раз, когда нажимается кнопка, интервал будет воссоздаваться, чего именно он хотел бы избежать.

2. Почему интервал будет воссоздан заново? useEffect по-прежнему запускается только один раз, но на этот раз он обращается к ссылке при каждом вызове обратного вызова. Вызывается ли setInterval более одного раза?

Ответ №3:

Попробуйте изменить свой эффект использования на что-то подобное. Почти каждую секунду он удаляет старый тайм-аут и создает новый, поэтому он должен иметь новое значение x

 useEffect(() => {
  const timer = setTimeout(() => {
    console.log(x);
    setX(x   1)
  }, 1000);
  // Clear timeout if the component is unmounted
  return () => clearTimeout(timer);
});
 

Комментарии:

1. Это не то, о чем спрашивает ОП, хотя он действительно забыл убраться.

2. Я просто следил за тем, что было у ОП в их собственном коде, но изменил его, чтобы правильно ответить на него.

3. Он явно упоминает, что не хочет использовать x в качестве массива зависимостей.

Ответ №4:

useEffect принимает массив зависимостей в качестве второго аргумента. Любые значения зависимого состояния/реквизита/контекста, используемые в useEffect, должны передаваться в этом массиве.

пример :

 
function App () {
 const [name, setName] = useState("")
 
 useEffect(() => {
     console.log("name ", name) // returns ""
 },[])

 
 useEffect(() => {
     console.log("name ", name) // returns  new value in name
 },[name])

 return (

  <div>
    {... some jsx}
  </div>
 )

}
 

В вашем случае вы используете x state in useEffect .Поэтому вам нужно пройти x как зависимость.

подобный этому :

 const [x, setX] = useState(0);
  useEffect(() => {
    //here is some code that I want to run only once.
    setInterval(() => {
      console.log(x);
      //problem is here x not updating
    }, 1000);
  }, [x]);

 

Ответ №5:

Всякий раз, когда вы обновляете состояние с помощью крючков, связанных с предыдущим значением, например, x 1, полагаясь на x, при его настройке следует использовать функцию обратного вызова.

Это гарантирует, что вы всегда ссылаетесь на самую последнюю версию состояния.

 setX(prevX => prevX   1);
 

Кроме того, если вы запускаете эффект использования только один раз, то x не будет обновляться, потому что он не будет распечатан после первого раза.

Если вы хотите, чтобы он печатался каждый раз при изменении x, вам нужно добавить x в качестве зависимости. setInterval покажет только начальное состояние x, потому что эффект использования никогда не получит его новое значение.

Попробуйте любой из них. Я бы предложил второй вариант:

 useEffect(() => {
    setInterval(() => {
      console.log(x);
    }, 1000);
  }, [x]);

useEffect(() => {
    console.log(x);
}, [x]);