#javascript #reactjs
#javascript #reactjs
Вопрос:
import React, { useEffect, useState } from "react";
export default function App() {
let [button, setButton] = useState(null);
let [num, setNum] = useState(5);
function revealState() {
console.log(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>
</>
);
}
Нажатие на кнопку «Log state» успешно регистрирует num
состояние. Нажатие на кнопку «Изменить состояние» успешно изменяет num
состояние. Повторное нажатие кнопки «Войти в состояние» не регистрирует обновленное значение состояния — оно регистрирует старое.
- Почему это так? Я предполагаю, что, поскольку
useEffect
выполняется только один раз, он ссылается только на первуюrevealState
функцию, которая ссылается только на первуюnum
переменную. Поскольку этого нет вreturn
инструкции компонента, он не обновляется. - Какова бы ни была причина проблемы, какие есть обходные пути? Некоторые из требований:
- тег не может быть отображен непосредственно в
return
инструкции. - у нас должно быть
useEffect
то, что есть, и у него должен быть какой-то массив dep (нежелательно, чтобы он запускался каждый раз, когда функциональный компонент выполняется повторно). - В реальном проекте могут быть внесены некоторые важные изменения в
useEffect
рендеринг обратного вызова тегов — поэтому нецелесообразно повторно запускатьuseEffect
, помещая что-то подобноеnum
в его массив dep.
Комментарии:
1. Почему вы монтируете элементы из крючка useEffect?
2. удалите этот пустой массив в конце useEffect() и сообщите мне результат
3. @theTradeCoder Второе предложение в пункте 2 дает вам понять, что я хотел бы сохранить это
4. Используя этот массив в конце, он заставляет useEffect() запускаться только один раз при каждой загрузке / перезагрузке
5. проверьте ответ ниже, помогает ли это вашей потребности
Ответ №1:
IMO, самое правильное решение — просто добавлять обновленный прослушиватель событий при каждом отображении страницы:
useEffect(() => {
el.onclick = onClickHandler
});
Прослушиватель событий всегда имеет доступ к последнему состоянию (и реквизитам). IMO, это решение более масштабируемо, чем ранее упомянутые решения — если мой прослушиватель событий должен отслеживать последние версии нескольких состояний и реквизитов, это может привести к беспорядку. При этом все, что мне нужно сделать, это добавить дополнительных слушателей в этот один обратный вызов useEffect. Мысли?
Ответ №2:
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.log(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>
</>
);
}
вы можете прослушать revealState в useEffect . который инициализируется только при изменении num, достигаемом с помощью useCallback. поэтому всякий раз, когда вы нажимаете кнопку, изменяется число, которое инициализирует функцию revealState и не инициализируется на других отправителях
Комментарии:
1. Работает, но я не могу запустить его повторно
useEffect
, потому что некоторые важные изменения в тегах могут быть внесены в другом месте. Я изменил свой вопрос, сказав, что решение должно отражать это. Но все равно спасибо.
Ответ №3:
вы должны добавить num
в качестве зависимости к useEffect
:
useEffect(() => {
const el = (
<button id="logStateButton" onClick={revealState}>
Log state
</button>
);
setButton(el);
}, [num]);
Комментарии:
1. Это действительно работает, однако я не могу повторно запустить
useEffect
его, так как могут быть внесены некоторые важные изменения в теги. Я изменил свой вопрос, чтобы включить это сейчас. Хотя спасибо. Возможно, вы могли бы взглянуть на мою попытку решения последней ссылки на песочницу, указанной в вопросе? Интересно, является ли моя проблема стандартной проблемой и есть ли стандартное решение.
Ответ №4:
После дополнительных разъяснений по вашей проблеме, похоже, вам нужно следить как за числом, так и за состоянием HTML. Объединение кода Алана и Кишора вместе — вот решение проблемы. Поскольку useEffect отслеживает только число в ответе Алана, поэтому, если какие-либо изменения в теге не приведут к его повторному запуску. кишор также упомянул еще одно исправление, которое заключается в использовании useCallback, но то, что нужно смотреть, — это the button
, а не the 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
изменении состояния.
Комментарии:
1. Это было бы слишком подробно, если бы у нас было много обработчиков onClick с разными обратными вызовами. Действительно ли я хочу каждый раз перерисовывать теги заново? Особенно, когда важные изменения в моих тегах, возможно, произошли из других источников, которые я теперь буду перезаписывать? Конечно, должно быть что-то попроще.