Изменение состояния реакции происходит повторным рендерингом позже, тогда это должно

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

  

Процесс будет:

  1. При щелчке use и вызове handleClick он отправит setIndex новый id
  2. index Изменится на то же самое id
  3. 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 работает и настолько простое.