import React, { useEffect, useState } from "react";
export default function App() {
let [button, setButton] = useState(null);
let [num, setNum] = useState(5);
function revealState() {
console.info(num);
}
function changeState() {
setNum(Math.random());
}
useEffect(() => {
const el = (
<button id = "logStateButton" onClick = {revealState}>
Log state
</button>
);
setButton(el);
}, []);
return (
<>
{button}
<button onClick = {changeState}>Change state</button>
</>
);
}
Нажатие на кнопку «Журнал состояния» успешно регистрирует состояние num
. Нажатие на кнопку «Изменить состояние» успешно изменяет состояние num
. Повторное нажатие кнопки «Журнал состояния» не регистрирует обновленное значение состояния — оно регистрирует старое.
Почему это? Я предполагаю, что, поскольку useEffect
запускается только один раз, он ссылается только на первую revealState
функцию, которая ссылается только на первую num
переменную. Поскольку его нет в операторе return
компонента, он не «обновляется».
Какова бы ни была причина проблемы, какие есть обходные пути? Вот некоторые из требований:
return
.useEffect
, который есть, и он должен иметь какой-то массив dep (нежелательно, чтобы он срабатывал каждый раз, когда компонент функции повторно выполняется).useEffect
, поэтому нецелесообразно повторно запускать useEffect
, помещая что-то вроде num
в его массив dep.🤔 А знаете ли вы, что...
JavaScript поддерживает работу с куки и хранилищем веб-браузера для сохранения данных на клиентской стороне.
import React, { useEffect, useState, useCallback } from "react";
export default function App() {
let [button, setButton] = useState(null);
let [num, setNum] = useState(5);
const revealState = useCallback(() => {
console.info(num);
}, [num])
function changeState() {
setNum(Math.random());
}
useEffect(() => {
const el = (
<button id = "logStateButton" onClick = {revealState}>
Log state
</button>
);
setButton(el);
}, [revealState]);
return (
<>
{button}
<button onClick = {changeState}>Change state</button>
</>
);
}
вы можете прослушать состояние раскрытия в useEffect. который инициализируется только при изменении num с помощью useCallback. поэтому всякий раз, когда вы нажимаете кнопку, число изменяется, что инициализирует функцию раскрыть состояние и не инициализируется при других повторных рендерингах.
вы должны добавить num
в качестве зависимости к useEffect
:
useEffect(() => {
const el = (
<button id = "logStateButton" onClick = {revealState}>
Log state
</button>
);
setButton(el);
}, [num]);
После дополнительных разъяснений по вашей проблеме кажется, что вам нужно следить как за числом, так и за состоянием HTML. Решением является объединение кода Алана и Кишора. Поскольку useEffect только наблюдает за числом в ответе Алана, поэтому любые изменения в теге не приведут к его повторному запуску. kishore также упомянул другое исправление, которое заключается в использовании useCallback, но нужно следить за button
, а не num
. Так:
const updateButton = useCallback(function (newButton) {
setButton(newButton);
}, [button])
useEffect(() => {
const el = (
<button id = "logStateButton" onClick = {revealState}>
Log state
</button>
);
updateButton(el)
}, [num]);
Это скажет useEffect наблюдать num
и вернет новый button
только при изменении состояния button
.
IMO, самое изящное решение - просто добавить обновленный прослушиватель событий каждый раз, когда страница отображается:
useEffect(() => {
el.onclick = onClickHandler
});
Слушатель событий всегда имеет доступ к последнему состоянию (и свойствам). IMO, это решение более масштабируемо, чем ранее упомянутые решения - если моему прослушивателю событий необходимо отслеживать последние версии нескольких состояний и реквизитов, это может стать грязным. При этом все, что мне нужно сделать, это добавить дополнительных слушателей в этот обратный вызов useEffect. Мысли?