React 的 useEffect 的一些使用场景和技巧
By 蚊子
我们知道 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 元素等。
想参与讨论,或者查看更多优质的文章: