具有useEffect的ReactJS无法呈现每个状态

时间:2019-01-02 00:02:25

标签: javascript reactjs react-hooks

我试图通过使用React来控制React的新useEffecthook”来控制模态进入和退出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. 在初始渲染中,animStatenull,因此我们返回null

  2. 将isOpen设置为true时,将触发效果并满足第一个条件,并将animState设置为“ entering”。在这种状态下,组件应返回不透明0的DOM。

  3. 该效果再次触发,并且满足第二个条件,将animState设置为“ entered”。创建具有1的不透明度的DOM,并使用CSS过渡进行动画处理。

据我所知,问题在于DOM添加时的不透明度为1,就好像React在批处理多个渲染调用并且仅更新DOM一次一样。如果您看一下代码笔,我正在每个渲染上记录animState,并且“ closed”状态正在记录,但似乎没有在显示。

会是这种情况吗?在再次运行效果之前,如何确保已发生渲染?

我想知道是否有一种更好的方法来做这样的事情?但是我也很想知道React在这里的幕后行为以及为什么我没有获得入门动画。

2 个答案:

答案 0 :(得分:0)

仅仅为不透明度设置动画似乎有点复杂,为什么不将不透明度存储在这样的状态变量中:

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

答案 1 :(得分:0)

我相信React确实在做您期望的事情。是浏览器的行为有些令人惊讶。

来自Using CSS transitions

  

在紧接以下情况使用转换时,应当心:

     
      
  • 使用.appendChild()将元素添加到DOM
  •   
  • 删除元素的显示:无;财产。
  •   
     

这被视为初始   状态从未发生过,元素始终处于最终状态   州。克服此限制的简单方法是应用   window.setTimeout()几毫秒,然后才更改   您打算过渡到的CSS属性。

如果我更改:

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

收件人:

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

它似乎工作正常。对我来说,如果我使用的超时时间少于8毫秒,它有效,并且该行为的具体情况可能因浏览器而异。

这是修改后的代码笔:https://codepen.io/anon/pen/KbQoZO?editors=0010