useEffect React挂钩将在每次更改时运行传入的函数。可以对其进行优化,使其仅在所需属性更改时调用。
如果我想从componentDidMount
调用初始化函数而不在更改时再次调用该怎么办?假设我要加载一个实体,但是加载功能不需要组件中的任何数据。我们如何使用useEffect
钩子做到这一点?
class MyComponent extends React.PureComponent {
componentDidMount() {
loadDataOnlyOnce();
}
render() { ... }
}
带有钩子,可能看起来像这样:
function MyComponent() {
useEffect(() => {
loadDataOnlyOnce(); // this will fire on every change :(
}, [...???]);
return (...);
}
答案 0 :(得分:107)
如果只想在初始渲染后运行赋予useEffect
的函数,则可以给它一个空数组作为第二个参数。
function MyComponent() {
useEffect(() => {
loadDataOnlyOnce();
}, []);
return <div> {/* ... */} </div>;
}
答案 1 :(得分:10)
将空数组作为第二个参数传递给useEffect
。这有效地告诉了React,并引用了文档:
这告诉React您的效果不依赖于道具或状态的任何值,因此它不需要重新运行。
下面是一个片段,您可以运行它以显示它的工作原理:
function App() {
const [user, setUser] = React.useState(null);
React.useEffect(() => {
fetch('https://randomuser.me/api/')
.then(results => results.json())
.then(data => {
setUser(data.results[0]);
});
}, []); // Pass empty array to only run once on mount.
return <div>
{user ? user.name.first : 'Loading...'}
</div>;
}
ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>
答案 2 :(得分:8)
TL; DR
useEffect(yourCallback, [])
-仅在第一次渲染后才触发回调。
详细说明
useEffect
默认在每次渲染组件后运行(从而产生效果)。
在组件中放置useEffect
时,您告诉React您想运行回调作为效果。 React将在渲染后和执行DOM更新后运行效果。
如果仅传递回调-回调将在每次渲染后运行。
如果传递第二个参数(数组),React将在第一个渲染之后以及每次更改数组中的一个元素时运行回调。例如,放置useEffect(() => console.log('hello'), [someVar, someOtherVar])
时-回调将在第一个渲染之后以及someVar
或someOtherVar
中的一个被更改的渲染之后运行。
通过向第二个参数传递一个空数组,React将在每次渲染该数组之后进行比较,并且不会看到任何更改,因此仅在第一个渲染之后才调用回调。
答案 3 :(得分:2)
我喜欢定义一个mount
函数,它以与useMount
相同的方式欺骗EsLint,我发现它更不言自明。
const mount = () => {
console.log('mounted')
// ...
const unmount = () => {
console.log('unmounted')
// ...
}
return unmount
}
useEffect(mount, [])
答案 4 :(得分:1)
仅在组件挂载后才运行函数是一种常见的模式,它证明了自己的钩子隐藏了实现细节。
const useMountEffect = (fun) => useEffect(fun, [])
然后在代码中的任何地方使用它。
function MyComponent() {
useMountEffect(() => {
loadDataOnlyOnce()
});
return (...);
}
关于useMountEffect挂钩
在将useEffect
与第二个数组参数一起使用时,React将在挂载(初始渲染)后以及数组中的一个或多个值更改后运行回调。由于我们传递了一个空数组,因此它将仅在安装后运行。
useMountEffect
实现了React挂钩的构想-基于本机/原始挂钩组成自定义挂钩。
答案 5 :(得分:0)
我们在GitHub上开发了一个模块,该模块具有用于获取数据的钩子,因此您可以按以下目的使用它:
import { useFetching } from "react-concurrent";
const app = () => {
const { data, isLoading, error , refetch } = useFetching(() =>
fetch("http://example.com"),
);
};
您可以提出要求,但欢迎任何PR。 https://github.com/hosseinmd/react-concurrent#react-concurrent
答案 6 :(得分:0)
将依赖项数组留为空白。希望这可以帮助您更好地理解。
useEffect(() => {
doSomething()
}, [])
空依赖项数组只能在Mount上运行一次
useEffect(() => {
doSomething(value)
}, [value])
通过value
作为依赖项。如果自上次以来依存关系发生了变化,效果将再次运行。
useEffect(() => {
doSomething(value)
})
无依赖性。每次渲染后都会调用它。
答案 7 :(得分:0)
function useOnceCall(cb, condition = true) {
const isCalledRef = React.useRef(false);
React.useEffect(() => {
if (condition && !isCalledRef.current) {
isCalledRef.current = true;
cb();
}
}, [cb, condition]);
}
并使用它。
useOnceCall(()=>{
console.log('called');
})
或
useOnceCall(()=>{
console.log('isLoading');
},isLoading);
答案 8 :(得分:0)
这是我对 Yasin 的回答的版本。
import {useEffect, useRef} from 'react';
const useOnceEffect = (effect: () => void) => {
const initialRef = useRef(true);
useEffect(() => {
if (!initialRef.current) {
return;
}
initialRef.current = false;
effect();
}, [effect]);
};
export default useOnceEffect;
用法:
useOnceEffect(
useCallback(() => {
nonHookFunc(deps1, deps2);
}, [deps1, deps2])
);