ReactJS с useEffect не рендерит каждое состояние - PullRequest
0 голосов
/ 02 января 2019

Я пытаюсь выяснить новый useEffect " крючок" React, используя его для управления модальным входом и выходом из DOM с некоторой анимацией.

Вот рабочий код:

https://codepen.io/nicholasstephan/pen/roprgw

и упрощенная версия:

const isOpen = props.isOpen;

const [ animState, setAnimState ] = useState(null);

useEffect(() => {
  if(isOpen === true && animState === null) {
    setAnimState('entering');
  }

  if(isOpen === true && animState === 'entering') {
    setAnimState('entered');
  }

}, [isOpen, animState]);

if(animState === null) {
  return null;
}

return (
  <div 
    style={{
      opacity: animState === 'entered' ? 1 : 0, 
      transition: 'opacity 200ms ease-in-out'
    }}>
    ...
  </div>
);

Я думаю:

  1. На начальном рендере animState равно null, поэтому мы возвращаем null.

  2. Когда для isOpen задано значение true, эффект срабатывает и выполняется первое условие, устанавливая animState на «вход».В этом состоянии компонент должен вернуть некоторое значение DOM с непрозрачностью 0.

  3. Эффект срабатывает снова, и выполняется второе условие, устанавливая animState в 'Введено'.Создание DOM с непрозрачностью 1, анимированной с помощью CSS-перехода.

Проблема, насколько я могу судить, заключается в том, что DOM добавляется с непрозрачностью 1, как будто React пакетирует несколько вызовов рендеринга и обновляет DOM только один раз.,Если вы заглянете в кодовое окно, я записываю animState на каждом рендере, а «закрытое» состояние - это логирование, но, похоже, это не рендеринг.

Может ли это иметь место?Как мне убедиться в том, что рендер произошел до того, как снова запустить эффект?

Мне было бы интересно услышать, если есть какой-то лучший способ сделать что-то подобное?Но мне также любопытно, что React делает здесь за кадром и почему я не получаю анимацию входа.

Ответы [ 2 ]

0 голосов
/ 04 января 2019

Я считаю, что React делает именно то, что вы ожидаете.Это браузер, который имеет немного удивительное поведение.

С Использование CSS-переходов :

Следует соблюдать осторожность при использовании перехода сразу после:

  • добавление элемента вDOM, использующий .appendChild ()
  • , удаляющий отображение элемента: нет;свойство.

Это обрабатывается так, как если бы начальное состояние никогда не возникало, а элемент всегда находился в своем конечном состоянии.Самый простой способ преодолеть это ограничение - применить window.setTimeout () за несколько миллисекунд до изменения свойства CSS, на которое вы собираетесь перейти.

Если я изменю:

if(status === true && animState === 'closed') {
  setAnimState('open');
}

to:

if(status === true && animState === 'closed') {
  setTimeout(()=>setAnimState('open'), 25);
}

кажется, что работает нормально.Для меня это НЕ сработало, если я использовал тайм-аут менее 8 миллисекунд, и особенности этого поведения, вероятно, варьируются от браузера к браузеру.

Вот модифицированный код: https://codepen.io/anon/pen/KbQoZO?editors=0010

0 голосов
/ 02 января 2019

Это кажется слишком сложным только для анимации непрозрачности, почему бы не сохранить непрозрачность в переменной состояния следующим образом:

const isOpen = props.isOpen;
const [ opacity, setOpacity ] = useState(0);

useEffect(() => {
    setOpacity(isOpen ? 1 : 0);      
}, [isOpen]);  

return (
  <div 
    style={{
      opacity, 
      transition: 'opacity 200ms ease-in-out'
    }}>
   ...
  </div>
); 

Вы также можете попробовать подключить useRef, чтобы обновить прозрачность вручную

const isOpen = props.isOpen;
const divEl = useRef(null);

useEffect(() => {
  if (divEl) {
    divEl.current.style.opacity = 1;
  }
}, [divEl]);

if (!isOpen) {
  return null;
}    

return (
  <div 
    ref={divEl}
    style={{
      opacity: 0, 
      transition: 'opacity 200ms ease-in-out'
    }}>
   ...
  </div>
); 
...