我想制作一个自定义钩子,该钩子将跳过第一个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元素。 这意味着我们无法静态验证您是否通过了 正确的依赖关系。 (反应钩/详尽的下降)
完整的示例代码:
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);
答案 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