#javascript #reactjs #use-state #react-state #react-state-management
#javascript #reactjs #использовать-состояние #react-state #управление состоянием реакции
Вопрос:
У меня есть функциональный компонент, управляющий несколькими состояниями, среди прочего состоянием, в котором хранится индекс, с помощью которого я отображаю тип таблицы, при нажатии на подходящую кнопку. При нажатии на эту кнопку вызывается функция обратного вызова, которая запускает обработчик щелчков. Этот обработчик щелчков изменяет состояние индекса на тот же ‘index’, что и запись массива, в котором я храню объект с информацией для рендеринга дочернего компонента.
Я бы ожидал, что onClick состояние изменится до того, как произойдет рендеринг, поэтому компонент может отображаться правильно. Тем не менее, это происходит только на рендеринг позже.
Я уже пытался вызвать useEffect-hook для повторного отображения при изменении состояния индекса, но это тоже не помогло.
Вот сокращенная версия кода:
export const someComponent = () => {
[index, setIndex] = useState(-1);
const handleClick = (id) => {
setIndex(id);
// This is a function, I use to render the table
buildNewComponent(index);
}
}
Далее «вниз» в коде я получил функцию, которая выполняет рендеринг записей таблицы. Там я передаю реквизит onClick в дочерний компонент таблицы следующим образом:
<SomeEntryComponent
onClick={() => handleClick(arrayEntry.id)}
>
// some code which is not affecting my problem
</SomeEntryComponent>
Итак, как было сказано: когда срабатывает этот onClick, он сначала отображает компонент при повторном нажатии на него, потому что сначала изменяется состояние.
Кто-нибудь может сказать мне, почему это происходит именно так и как я мог бы исправить это, чтобы оно работало должным образом?
Комментарии:
1. Вместо передачи индекса из состояния я бы передал id из аргумента. вы вызываете buildNewComponent внутри той же функции, поэтому просто передайте туда идентификатор, а не индекс
2.Подумайте об этом в базовом потоке управления JS: когда между
setIndex(id);
иbuildNewComponent(index);
в следующей строке, как вы думаетеindex
, было переназначено?[index, setIndex] =
не может быть достигнуто до следующего вызова функции, то есть при следующем рендеринге компонента. Просто используйтеid
.
Ответ №1:
Как указывали другие, это проблема синхронности, когда index
обновление происходит после buildComponent
вызова.
Но с точки зрения дизайна было бы лучше утверждать index
существование по его значению, а не помечать его в обработчике. Я не знаю деталей, стоящих за buildComponent
, но вы можете превратить это в условный рендеринг компонента.
Рендеринг вашего компонента становится производным от его данных, в отличие от создания вручную.
export const someComponent = () => {
[index, setIndex] = useState(-1);
const handleClick = (id) => setIndex(id);
const indexHasBeenSelected = index !== -1
return (
<div>
{indexHasBeenSelected amp;amp; <NewComponent index={index} />}
</div>
)
}
Комментарии:
1. Итак, просто для меня, чтобы понять: почему состояние индекса теперь обновляется при первом «щелчке»?
2. Монтирование
<NewComponent />
теперь «закрыто» значениемindex
. Проверка равенстваindex !== -1
всегда будет завершаться неудачей, покаindex
не будет обновлено. Это означает, что при<NewComponent />
монтированииindex
гарантируется, что он был обновлен.3. Теперь я понял, еще раз спасибо. Но разве это не должно как-то работать, без реализации этой проверки «изменения состояния» самостоятельно? Разве не в этом вся идея того, как работают состояния react?
4. @Robby Это должно произойти и происходит. Но, исходя из ваших формулировок / потребностей, похоже, что компонент не должен существовать до тех пор, пока не будет нажата запись. Чтобы скрыть это и постоянно обновлять индекс, вам нужна эта реализация
Ответ №2:
При вызове buildNewComponent
index
еще не обновлено. Вы только что вызвали setState, нет гарантии, что значение будет обновлено сразу после этого. Вы могли бы использовать id
здесь или вызвать buildNewComponent
внутри useEffect, который имеет index
в качестве своей зависимости.
Ответ №3:
Я считаю, что у вас может быть правильное поведение, если вы используете useEffect
и отслеживаете index
изменения.
export const someComponent = () => {
[index, setIndex] = useState(-1);
useEffect(() => {
// This is a function, I use to render the table
buildNewComponent(index);
}, [buildNewComponent, index])
const handleClick = (id) => {
setIndex(id);
}
}
Процесс будет:
- При щелчке use и вызове
handleClick
он отправитsetIndex
новыйid
index
Изменится на то же самоеid
useEffect
Увидит изменение вindex
и вызоветbuildNewComponent
.
Одна важная вещь — обернуть buildNewComponent
с useCallback
, чтобы избежать неожиданного поведения.
Комментарии:
1. Я не думаю, что
buildNewComponent
это должно быть частью проверки равенстваuseEffect
. Это зависит от реализации, но это либо 1. Всегда не выполняется проверка на равенство или 2. Никогда не изменяется2. @Andrew Я согласен с вами, поскольку я не знаю, что внутри
buildNewComponent
, я должен был упомянуть об этом.3. Как бы мне обернуть это правильным образом? Если я оберну это в дополнительный useEffect, он сообщит мне в другом useEffect, что он не может найти buildNewComponent (конечно …). Может быть, для меня уже слишком поздно, но я не могу представить, как я это сделаю..
4. @Robby У меня нет дополнительной информации, чтобы точно сказать вам, что делать, но одна идея состоит в том, чтобы заново продумать стратегию, лежащую в основе
buildNewComponent
. Кажется, чтоindex
может использоваться как компонентprops
, и вы можете работать с деревом компонентов, которое сохранит состояние и перейдет к следующему компоненту.5. @VictorBarros Я попробую использовать вашу идею, потому что это кажется более «реактивным способом», даже если решение Andres работает и настолько простое.