Реагирующие перехватчики ESLint - правило "исчерпывающей зависимости" - неужели нет лучшего способа?
Информации об этом правиле довольно много, но я действительно думаю, что должен быть улучшен способ обработки альтернативы componentDidMount.
Похоже, что это сбивает с толку и многих разработчиков, и довольно сложно написать обходные пути для наиболее распространенных случаев использования:
https://github.com/facebook/react/issues/14920
Здесь я привожу несколько примеров, которые действительно нелегко настроить с помощью этого правила:
Пример 1: Инициализация приложения - или проверьте файлы cookie /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: Использование функций, связанных с хуками. Пример с response-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. Когда данные загружаются или приходит ошибка - я запускаю сообщение toastr с функцией перевода t, которую я получаю в ловушке. Но я не хочу получать новые данные каждый раз, когда пользователь меняет язык. Но eslint хочет, чтобы я помещал t в массив deps, что приводит к повторной визуализации компонента и загрузке новых данных каждый раз, когда пользователь переключает язык. Более того, мы должны ожидать, что в будущем появится все больше и больше таких функций-перехватчиков, которые мы, возможно, захотим использовать в useEffects.
Пример 3: Одна переменная зависит от других переменных
useEffect(() => {
if (error === null) {
const data = doSomethingWithMyValue(value1);
setMyData(data);
} else {
showSomeErrorText(); //or do nothing
}
}, [value1]);
Если я хочу, чтобы это происходило только при изменении значения 1, функция doSomethingWithMyValue будет запущена, но не при изменении ошибки. Когда мы помещаем ошибку вuseEffects
deps, тогда вся логика будет нарушена, потому что useEffect будет запускаться каждый раз при изменении ошибки. А этого я не хочу.
Я также попытался найти обходной путь с установленной переменной и useCallback
, но мне кажется, что useEffect
срабатывает лишнее много раз и как-то все медленнее.
Так есть ли уловка логики или нам действительно нужно использовать всю эту записку и useCallbacks
и т.д., чтобы предотвратить серьезную проблему?
Спасибо за любой отзыв!