跳过第一次useEffect渲染自定义钩子

时间:2019-11-02 17:28:06

标签: reactjs react-hooks

我想制作一个自定义钩子,该钩子将跳过第一个useEffect渲染并在各处重复使用。

我做了一个自定义钩子的工作示例:

function useEffectSkipFirst(fn, arr) {
  const isFirstRun = useRef(true);

  useEffect(() => {
    if (isFirstRun.current) {
      isFirstRun.current = false;
      return;
    }

    fn();
  }, [...arr]);
}

使用方法:

useEffectSkipFirst(fn, []);

问题是我收到一些警告,我试图理解这些警告,希望您能对我有所帮助:

  

React Hook useEffect缺少依赖项:'fn'。要么包括它   或删除依赖项数组。如果“ fn”更改过于频繁,请找到   定义它并包装该定义的父组件   useCallback。 (反应钩/穷尽滴水)结冰

     

React Hook useEffect在其依赖项数组中具有一个split元素。   这意味着我们无法静态验证您是否通过了   正确的依赖关系。 (反应钩/详尽的下降)

Codesandbox example.

完整的示例代码:

import React, { useRef, useEffect, useState } from "react";
import ReactDOM from "react-dom";

function useEffectSkipFirst(fn, arr) {
  const isFirstRun = useRef(true);

  useEffect(() => {
    if (isFirstRun.current) {
      isFirstRun.current = false;
      return;
    }

    fn();
  }, [...arr]);
}

function App() {
  const [clicks, setClicks] = useState(0);
  const [date, setDate] = useState(Date.now());
  const [useEffectRunTimes, setTseEffectRunTimes] = useState(0);

  useEffectSkipFirst(() => {
    addEffectRunTimes();
  }, [clicks, date]);

  function addEffectRunTimes() {
    setTseEffectRunTimes(useEffectRunTimes + 1);
  }

  return (
    <div className="App">
      <div>clicks: {clicks}</div>
      <div>date: {new Date(date).toString()}</div>
      <div>useEffectRunTimes: {useEffectRunTimes}</div>
      <button onClick={() => setClicks(clicks + 1)}>add clicks</button>
      <button onClick={() => setDate(Date.now())}>update date</button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

3 个答案:

答案 0 :(得分:1)

您用作回调useEffectSkipFirst的fn函数在每个渲染器上都会改变。同样,由于在useEffectSkipFirst中调用了此函数,因此linter会提示您将其定义为依赖项。

但是,在实现它的方式上,要取决于实现useEffectSkipFirst的代码,以确保所有值都具有正确的闭包,并且可以跳过将其定义为对useEffect的依赖。但是,如果这样做,可能会导致无限循环。

您也可以查看此信息以了解更多详细信息:How to fix missing dependency warning when using useEffect React Hook?

答案 1 :(得分:1)

第一个警告基本上意味着useEffect回调中引用的所有依赖项都必须在其依赖项数组中声明。 The official documentation提供了有关将函数添加到依赖项数组的更多说明。

第二次警告意味着您不能动态指定依赖项,即以某种方式使依赖项直到运行时才知道。这是因为在代码执行之前对依赖项进行了静态比较,因此必须明确说明它们。

答案 2 :(得分:1)

我看到了您问题的一种解决方案。

首先,您需要将useEffect依赖项数组减少为单个值(通过使用钩子import UIKit class AddProductsViewController: UIViewController { @IBOutlet weak var productName: UILabel! @IBOutlet weak var priceForKg: UILabel! @IBOutlet weak var productImage: UIImageView! override func viewDidLoad() { super.viewDidLoad() } @IBAction func addProduct(_ sender: UIButton) { self.navigationController?.popViewController(animated: true) } } )。

然后,实际上,您应该使用useMemo存储此依赖项值的旧值,这样,只有在依赖项发生更改时,您才能调用useRef函数 / strong>。

这是自定义钩子的代码:

fn

以下是使用function useEffectSkipFirst(fn, depValue) { const oldDepValueRef = useRef(depValue); useEffect(() => { if (depValue !== oldDepValueRef.current) { // Note that this code will not be executed the first time as // we initialized oldDepValueRef with depValue fn(); oldDepValueRef.current = depValue; } }, [fn, depValue]); } 将数组dep减小为单个值的代码:

useMemo

如果仅在效果中使用// I decided the return an object but it could be an array or something else const depValue = useMemo(() => ({}), [clicks, date]); useEffectSkipFirst(addEffectRunTimes, depValue); function addEffectRunTimes() { // When the value depends on the previous value, // you can use a function to update the state value setTseEffectRunTimes(v => v + 1); } 函数,则可以直接编写:

addEffectRunTimes