#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]);