说明
dom监听事件的绑定和解绑只执行一次。在react hooks中实现,需要使用useEffect。
useEffect
简单了解下useEffect是做什么的?React 组件在渲染后执行某些操作( DOM 更新之后调用useEffect的第一个参数函数),还有默认情况下,它在第一次渲染之后和每次更新之后都会执行。
useEffect执行一次
useEffect第二个参数传入空的依赖数组 [],意味着该 hook 只在组件挂载时运行一次,并非重新渲染时。
useEffect绑定和解绑事件
简单以setInterval为例
useEffect(() => {
const id = setInterval(() => {}, 1000);
return () => clearInterval(id);
},[]);
问题:变量和state值不变且是初始值
但如此会有问题,在useEffect第一参数函数内,一般变量和state的值不会发生变化。因为当 effect 执行时,会创建一个闭包,并将 一般变量和state的值被保存在该闭包当中,且为初始值。
监听事件
下面以Orientation(横竖屏依赖模块)为例简单记录下解决方法
需求:监听横竖屏切换,触发事件拿到当前tabsIndex
起初,问题代码
const [tabsIndex, setTabsIndex] = useState(2);
// setTabsIndex()改变tabsIndex
useEffect(()=>{
const targetFn=()=>{
setTimeout(()=>{
scrollViewRef.current.scrollTo({ x: tabsIndex*width, animated: false });
},0);
}
Orientation.addOrientationListener(targetFn);
return () => Orientation.removeOrientationListener(targetFn);
},[]);
上面说过原因,targetFn中的tabsIndex一直是初始值2,不变。横竖屏切换时监听事件是触发了,但是内部tabsIndex值不正确,造成了无法滚动到指定位置。
解决方法1
/**组件外定义**/
const useOrientationListener=()=>{
const [triggerN,setTriggerN]=useState(0);
useEffect(()=>{
const targetFn=()=>setTriggerN(n=>n+1);
Orientation.addOrientationListener(targetFn);
return () => Orientation.removeOrientationListener(targetFn);
},[]);
return triggerN;
}
/**组件内使用**/
const [tabsIndex, setTabsIndex] = useState(2);
// setTabsIndex()改变tabsIndex
const oriN=useOrientationListener();
useEffect(() => {
setTimeout(() => {
scrollViewRef.current.scrollTo({ x: tabsIndex*width, animated: false });
}, 0)
}, [oriN]);
useOrientationListener内useEffect [] 保证了只监听一次事件,触发时,将触发次数这个state传递给组件的useEffect,useEffect会依赖oriN的变化而执行第一参数函数体
解决方法2
/**组件内部**/
const tabsIndex = useRef(2);
//tabsIndex.current 赋值
useEffect(()=>{
const targetFn=()=>{
setTimeout(()=>{
scrollViewRef.current.scrollTo({ x: tabsIndex.current *width, animated: false });
},0);
}
Orientation.addOrientationListener(targetFn);
return () => Orientation.removeOrientationListener(targetFn);
},[]);
利用了useRef的特性规避了内部state是初始值的问题,下面是部分知识点
- useRef 就像是可以在其 .current 属性中保存一个可变值的“盒子”
- useRef 会在每次渲染时返回同一个 ref 对象
- 当 ref 对象内容发生变化时,useRef 并不会通知你
- 变更 .current 属性不会引发组件重新渲染