带有 useEffect 的 ReactJS 不呈现每个状态
ReactJS with useEffect not rendering each state
我正在尝试通过使用 React 的新 useEffect
“hook”来控制模态进入和退出带有一些动画的 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>
);
我的想法是:
在初始渲染中,animState
是 null
所以我们 return null
。
当 isOpen 设置为 true 时,将触发效果并满足第一个条件,将 animState
设置为 'entering'。在这种状态下,组件应该 return 一些 DOM,不透明度为 0
。
效果再次触发并满足第二个条件,将 animState
设置为 'entered'。使用 1
的不透明度创建 DOM,使用 css 过渡动画。
据我所知,问题在于添加的 DOM 的不透明度为 1,就好像 React 正在批处理多个渲染调用并且只更新 DOM一次。如果你查看 codepen,我会在每次渲染时记录 animState
,并且 "closed" 状态正在记录,但它似乎没有在渲染。
难道是这样吗?我如何确保渲染在 运行 效果再次发生之前发生?
我很想知道是否有更好的方法来做这样的事情?但我也很好奇 React 在幕后做了什么,以及为什么我没有得到进入动画。
只为不透明度设置动画似乎有点太复杂,为什么不将不透明度存储在这样的状态变量中:
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>
);
我相信 React 正在做你所期望的。浏览器的行为有点令人惊讶。
Care should be taken when using a transition immediately after:
- adding the element to the DOM using .appendChild()
- removing an element's display: none; property.
This is treated as if the initial
state had never occurred and the element was always in its final
state. The easy way to overcome this limitation is to apply a
window.setTimeout() of a handful of milliseconds before changing the
CSS property you intend to transition to.
如果我改变:
if(status === true && animState === 'closed') {
setAnimState('open');
}
至:
if(status === true && animState === 'closed') {
setTimeout(()=>setAnimState('open'), 25);
}
它似乎工作正常。对我来说,如果我使用的超时时间少于 8 毫秒,它确实 NOT 起作用,并且这种行为的具体情况可能因浏览器而异。
我正在尝试通过使用 React 的新 useEffect
“hook”来控制模态进入和退出带有一些动画的 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>
);
我的想法是:
在初始渲染中,
animState
是null
所以我们 returnnull
。当 isOpen 设置为 true 时,将触发效果并满足第一个条件,将
animState
设置为 'entering'。在这种状态下,组件应该 return 一些 DOM,不透明度为0
。效果再次触发并满足第二个条件,将
animState
设置为 'entered'。使用1
的不透明度创建 DOM,使用 css 过渡动画。
据我所知,问题在于添加的 DOM 的不透明度为 1,就好像 React 正在批处理多个渲染调用并且只更新 DOM一次。如果你查看 codepen,我会在每次渲染时记录 animState
,并且 "closed" 状态正在记录,但它似乎没有在渲染。
难道是这样吗?我如何确保渲染在 运行 效果再次发生之前发生?
我很想知道是否有更好的方法来做这样的事情?但我也很好奇 React 在幕后做了什么,以及为什么我没有得到进入动画。
只为不透明度设置动画似乎有点太复杂,为什么不将不透明度存储在这样的状态变量中:
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>
);
我相信 React 正在做你所期望的。浏览器的行为有点令人惊讶。
Care should be taken when using a transition immediately after:
- adding the element to the DOM using .appendChild()
- removing an element's display: none; property.
This is treated as if the initial state had never occurred and the element was always in its final state. The easy way to overcome this limitation is to apply a window.setTimeout() of a handful of milliseconds before changing the CSS property you intend to transition to.
如果我改变:
if(status === true && animState === 'closed') {
setAnimState('open');
}
至:
if(status === true && animState === 'closed') {
setTimeout(()=>setAnimState('open'), 25);
}
它似乎工作正常。对我来说,如果我使用的超时时间少于 8 毫秒,它确实 NOT 起作用,并且这种行为的具体情况可能因浏览器而异。