Wenzi

React 的 useEffect 的一些使用场景和技巧

蚊子前端博客
发布于 2024/09/24 00:12
useEffect()是最常用的hooks之一,一起来看下它的使用场景!

我们知道 useEffect 的一些常规用法:

  1. 在组件挂载时执行一次,能够获取到真实的 dom 元素;
  2. 组件销毁时执行回调,可以清除和重置数据;

如:

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 元素等。

标签:reacthooks
阅读(373)
No data
No data