掌握React的useEffect:提高开发效率的4个技巧
我们知道 React 的 useEffect 有一些常规用法:
- 在组件挂载时执行一次,能够获取到真实的 dom 元素;
- 组件销毁时执行回调,可以清除和重置数据;
如:
const App = () => { useEffect(() => { if (open && values) { form.setFieldsValue(values); return () => form.resetFields(); } }, [open, values]); };
useEffect
的回调会在组件挂载完成时执行一次,后续若依赖项改变,则回调会再次执行。同时在组件卸载时可以清除副作用。基于这个特性,我们实现一些特定的功能。
1. 清除副作用
如定时器、JavaScript 原生事件等,在组件卸载时需要取消定时器和卸载原生事件。
const App = () => { useEffect(() => { const timer = setInterval(() => { console.log(Date.now()); }, 1000); return () => clearInterval(timer); }, []); useEffect(() => { const listener = () => { console.log(window.scrollX, window.scrollY); }; window.addEventListener("scroll", listener, false); return () => window.removeEventListener("scroll", listenter); }, []); };
若没有及时取消,在组件多次渲染时,可能会产生多个定时器或者绑定多个事件。
2. 获取初始值
我在自定义一些稍微复杂的 Form 表单组件时,经常会用到这个功能。
在Ant Design
的自定义 Form 表单时,Form 组件会把 value 和 onChange 传给自定义组件内,我们再进行获取。但因为是异步获取数据,form 表单的初始化有延迟,我们并不能马上获取到数据。因此可以监听传入的 value,当获取到 value 后就不再处理该数据。
const InputApp = ({ value: initValue, onChange }) => { const [value, setValue] = useState(initValue); const initedRef = useRef(false); // 是否获取到了初始值 useEffect(() => { if (initedValue && !initedRef.current) { // 已经有值并且还没初始操作 initedRef.current = true; setValue(initedValue); } }, [initValue]); };
3. 只监听数据的变化
useEffect()
会在组件初始化时执行一次回调函数,然后在依赖项发生变化时,也会执行回调函数。
但是,我们并不希望在组件初始化时执行回调函数,只想监听依赖的变化。我们可以像第 2 小节中那样,用一个标记来表示。
我们认为第一次执行,就是初始时执行;后续是依赖项变化时导致的
因此我们把第一次执行跳过即可。
const App = (props) => { const firstedRef = useRef(false); // 初始化的回调是否已执行 useEffect(() => { if (!firstedRef.current) { firstedRef.current = true; return; } console.log("props changed", props); }, [props]); };
4. 获取 DOM 元素
无论是使用 ref 属性
和useRef()
,还是原生的 js 方法,获取 DOM 元素,在 useEffect()
中都能保证获取到真实的 DOM 元素。如果是异步获取数据后再渲染 DOM 元素,可以在依赖项中监听对应的数据。
const App = () => { const domRef = useRef(null); useEffect(() => { // 通过 ref 属性 和 useRef() 获取 dom 元素 console.log(domRef.current); // 通过原生的 js 的方法获取 dom 元素 console.log(document.getElementById("title")); }, []); return ( <div ref={domRef}> <div id="title">前端博客</div> </div> ); };
若是通过异步获取数据后,然后再渲染的页面,可以监听相应的数据。
import useSWR from "swr"; const App = () => { const itemRefs = useRef([]); const { data } = useSWR("/api", async () => { const response = await fetch("/api"); return res.json(); }); useEffect(() => { if (data) { // 在这里,你可以访问 itemRefs.current 数组,它包含了所有的 DOM 元素 itemRefs.current.forEach((item, index) => { console.log(`Item ${index}:`, item); }); } }, [data]); return ( <div> {data?.map((item) => ( <div key={item.id} ref={(ele) => itemRefs.current.push(ele)}></div> ))} </div> ); };
5. 总结
在 React 中,使用 useEffect() 可以实现一些特殊的功能。如清除副作用、获取初始值、只监听数据的变化、获取 DOM 元素等。useEffect() 是 React 提供的一个钩子函数,用于在组件渲染后执行副作用操作。副作用操作包括订阅数据源、手动修改 DOM、网络请求等。通过传入一个回调函数给 useEffect(),可在组件的生命周期中执行特定的逻辑。可以传入一个空数组作为第二个参数,仅在组件挂载和卸载时执行一次,用于清除副作用。另外,可以传入一个依赖数组作为第二个参数,仅当依赖项发生变化时才执行回调函数。这样可以优化性能,避免不必要的重复执行。通过 useEffect(),我们可以更加灵活地控制组件的行为,实现各种功能需求。
码云笔记 » 掌握React的useEffect:提高开发效率的4个技巧