我只希望 useEffect 在我的依赖列表发生变化时运行,每次挂载组件时它也在运行,有什么办法可以在挂载时不触发吗?
你可以告诉 React 跳过应用效果,如果某些值 在重新渲染之间没有变化。
我最初认为这意味着它不应该在后续挂载上重新渲染,但这个问题澄清了这一点。
我在主"页面"(react-router(中显示记录列表,用户可以选择单个记录并转到详细信息页面,然后返回母版页 - 因此在这种情况下,主列表组件完全卸载/挂载。 每次加载"母版页"时,我都会看到正在获取的数据,我只希望在其中一个依赖项更改时发生这种情况;这些依赖项和数据本身存储在 Redux 中,因此它们是全局的。
useEffect 或其他钩子是否可以仅在依赖项更改时触发?
const {page, pageSize, search, sorts} = useSelector(getFilters);
const data = useSelector(getData);
useEffect(() => {
console.log("fetching");
dispatch(fetchData(page, pageSize, search, sorts));
}, [page, pageSize, search, sorts]);
您无法开箱即用地配置它。
但是,一种常见的模式是使用一些isMounted
标志,如下所示:
// Is Mounted
const useFetchNotOnMount = () => {
...
const isMounted = useRef(false);
useEffect(() => {
if (isMounted.current) {
console.log('fetching');
dispatch(fetchData(filters));
} else {
isMounted.current = true;
}
}, [dispatch, filters]);
};
// Same (Is First Render)
const useFetchNotOnMount = () => {
...
const isFirstRender = useRef(true);
useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
} else {
console.log("fetching");
dispatch(fetchData(filters));
}
}, [dispatch, filters]);
};
- 阅读更多深入的
useEffect
用法
如果最初有多个useEffect
要阻止运行,则可以执行以下操作:
export default function App() {
const mountedRef = useMountedRef();
const [isLoggedIn, setLoggedIn] = React.useState(false);
const [anotherOne, setAnotherOne] = React.useState(false);
React.useEffect(() => {
if (mountedRef.current) {
console.log("triggered", isLoggedIn);
}
}, [isLoggedIn]);
React.useEffect(() => {
if (mountedRef.current) {
console.log("triggered", anotherOne);
}
}, [anotherOne]);
React.useEffect(() => {
if (mountedRef.current) {
console.log("triggered", isLoggedIn, anotherOne);
}
}, [anotherOne, isLoggedIn]);
return (
<div>
<button onClick={() => setLoggedIn(true)}>Login</button>
</div>
);
}
const useMountedRef = () => {
const mountedRef = React.useRef(false);
React.useEffect(() => {
setTimeout(() => {
mountedRef.current = true;
});
}, []);
return mountedRef;
};
演示:https://stackblitz.com/edit/react-eelqp2
重要的一点是,您必须使用setTimeout
进行合理的延迟,以确保在所有初始使用效果之后将 ref 值设置为 true。
挂载后可以使用自定义钩子运行使用效果。
const useEffectAfterMount = (cb, dependencies) => {
const mounted = useRef(true);
useEffect(() => {
if (!mounted.current) {
return cb();
}
mounted.current = false;
}, dependencies); // eslint-disable-line react-hooks/exhaustive-deps
};
useEffectAfterMount(() => {
console.log("fetching");
dispatch(fetchData(page, pageSize, search, sorts));
}, [page, pageSize, search, sorts]);
这是typescript
版本:
const useEffectAfterMount = (cb: EffectCallback, dependencies: DependencyList | undefined) => {
const mounted = useRef(true);
useEffect(() => {
if (!mounted.current) {
return cb();
}
mounted.current = false;
}, dependencies); // eslint-disable-line react-hooks/exhaustive-deps
};
我知道这已经很晚了,但我认为还值得注意的是,对于 OP 试图实现的目标,数据缓存将是一个更全面和可扩展的解决方案。像 react-query 这样的库非常适合此。