关于这条规则有很多信息,但我真的认为应该有一些更好的方法来处理componentDidMount-alternative。
看起来它也让很多开发人员感到困惑,并且很难为最常见的用例编写变通方法:
https://github.com/facebook/react/issues/14920
在这里,我提供了一些确实不容易使用此规则设置的示例:
示例 1:应用程序的初始化 - 或在应用程序加载后检查 cookies/localstorage,并在上下文中设置它们:
import React, { useState, createContext, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import CookieHandler from '../helpers/CookieHandler';
import Loading from '../components/common/Loading';
const defaultValues = {
authorization: {
loggedIn: false,
isAdmin: false,
},
dispatchAuthorization: () => {},
};
const LoginContext = createContext(defaultValues);
const reducer = (state, authorization = { loggedIn: false, isAdmin: false }) => {
const { loggedIn, isAdmin } = authorization;
return { loggedIn: !!loggedIn, isAdmin: !!isAdmin };
};
const LoginContextProvider = ({ children }) => {
const [authorization, dispatchAuthorization] = React.useReducer(reducer, defaultValues.authorization);
const [mounted, setMounted] = useState(false);
const { i18n } = useTranslation();
const setLoginData = (token, isAdmin) => {
if (token) {
dispatchAuthorization({
loggedIn: true,
isAdmin: isAdmin,
});
}
setMounted(true);
};
const setLanguage = (language) => {
i18n.changeLanguage(language);
CookieHandler.setLanguage(language);
};
const loginUser = data => {
CookieHandler.setToken(data.token);
CookieHandler.setAdmin(data.isAdmin);
setLoginData(data.token, data.isAdmin);
};
const removeLoginData = () => {
CookieHandler.logout();
dispatchAuthorization({
loggedIn: false,
isAdmin: false,
});
};
useEffect(() => {
const token = CookieHandler.getToken();
const isAdmin = CookieHandler.getAdmin();
const language = CookieHandler.getLanguage();
if (token) {
setLoginData(token, isAdmin);
} else if (authorization.loggedIn || authorization.isAdmin) removeLoginData();
if (language) setLanguage(language);
setMounted(true);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (!mounted) return <Loading />;
return (
<LoginContext.Provider
value={{
authorization,
dispatchAuthorization,
setLoginData,
loginUser,
setLanguage,
logoutUser: removeLoginData,
}}
>
{children}
</LoginContext.Provider>
);
};
const LoginContextConsumer = LoginContext.Consumer;
export { LoginContext, LoginContextProvider, LoginContextConsumer };
在这里,我希望在加载应用程序后useEffect
真正调用它,而不再调用。但是根据 eslint 我应该在 dept 数组中添加授权变量,但这是我真的不想做的事情。因为每次授权发生变化,整个INIT-Prozess都会一遍遍的重新开始。我认为非常需要在组件/或应用程序启动后触发的初始化功能,而不考虑以后更改变量?
示例 2:使用来自钩子的函数。react-i18next的一个例子:
...
import { useTranslation } from 'react-i18next';
...
const MyComponent = ({ someId }) => {
const { t } = useTranslation();
const [data, setData] = useState(null);
useEffect(() => {
const someAsyncFetch = async () => {
try {
const data = await asyncFetchData({
someId,
});
setData(data);
toastr.success(t('your.data.was.fetched.successfully'));
} catch (e) {
console.log(e);
toastr.error(t('your.data.can.not.be.fetched'));
}
};
someAsyncFetch();
}, [someId]);
}
在此示例中,我想在渲染组件或更改 someId 时获取所有数据。当数据加载或出现错误时 - 我使用我在钩子中获得的翻译函数t触发 toastr 消息。但我不想每次用户切换语言时都获取新数据。但是 eslint 希望我将 t 放入 deps 数组中,这会导致每次用户切换语言时重新渲染组件并加载新数据。而且,我们应该预料到未来会出现越来越多的此类钩子函数,我们最终可能希望在 useEffects 中使用它们。
示例 3:一个变量依赖于其他变量
useEffect(() => {
if (error === null) {
const data = doSomethingWithMyValue(value1);
setMyData(data);
} else {
showSomeErrorText(); //or do nothing
}
}, [value1]);
如果我只希望在 value1 更改时,函数 doSomethingWithMyValue 将被触发,但不会在错误更改时触发。当我们将 error 放入useEffects
deps 数组时,整个逻辑就被破坏了,因为每次 error 发生变化时都会触发 useEffect。这是我不想要的。
我还尝试使用已安装的变量和 进行某种解决方法useCallback
,但是它看起来对我来说是useEffect
不必要的多次触发,并且不知何故整个事情变慢了。
那么是否存在逻辑问题或者我们真的需要使用所有这些备忘录useCallbacks
等来防止一些严重的问题?
感谢您的任何反馈!