Противоречие useEffects?

Sep 22 2020

Мне кажется, я часто сталкиваюсь с этой проблемой.

При связывании useEffects, которые должны запускаться после изменения состояния, я обнаружил, что некоторые из useEffects в цепочке имеют перекрывающиеся зависимости, которые заставляют их запускать оба, а не последовательно, после того, как один устанавливает состояние.

Пример: я запускаю цепочку useEffects при chapterIndexобновлении.

let [ chapterIndex, setChapterIndex ] = useState<number | null>(0);

let [textTransitioning, setTextTransitioning] = useState<'out' | 'in' | null>(null);

useEffect(() => {
    setTextTransitioning('out')
}, [chapterIndex])

useEffect(() => {
    if (chapterIndex !== null) {
        const {
            current: chapter = null
        } = chapterRefs.current[chapterIndex];
            
        if (textTransitioning === 'in') {
            chapter?.classList.add('in');
        }
    }
}, [textTransitioning, chapterIndex])

Как мне предотвратить запуск обоих при chapterIndexизменении? Я хочу, чтобы второй useEffect запускался только после того, как первый useEffect установил значение textTransitioning. При удалении chapterIndexиз массива зависимостей я получаю сообщение об ошибке: «React Hook useEffect имеет недостающую зависимость: chapterIndex». Либо включите его, либо удалите массив зависимостей ».

Ответы

Itaywazana Sep 22 2020 at 09:54

useEffectпринимает 2 аргумента. первая - это вызываемая функция, а вторая - массив переменных, называемый массивом зависимостей. второй аргумент, используемый для запуска эффекта использования при изменении одной из переменных в массиве. поэтому, если вы не хотите, useEffectчтобы вас вызывали при изменении конкретной переменной, не передавайте ее в массив зависимостей. ваш код должен быть таким:

let [ chapterIndex, setChapterIndex ] = useState<number | null>(0);

let [textTransitioning, setTextTransitioning] = useState<'out' | 'in' | null>(null);

useEffect(() => {
      setTextTransitioning('out')
}, [chapterIndex])

useEffect(() => {
 if (chapterIndex !== null) {
     const {
         current: chapter = null
     } = chapterRefs.current[chapterIndex];
         
     if (textTransitioning === 'in') {
         chapter?.classList.add('in');
     }
 }
}, [textTransitioning]

Я думаю, что заставлять один useEffectзапускать другой не очень эффективно, но, возможно, я не вижу всей картины

YogeshChaturvedi Sep 22 2020 at 09:58

Вам необходимо удалить chapterIndex из массива во втором useEffect. Поэтому, когда будет запущен первый useEffect, он обновит «textTransitioning», что вызовет второй триггер useEffect.

Для более условного выполнения useEffect вы можете обратиться к https://reactjs.org/docs/hooks-effect.html