我正在尝试使用钩子在我的App.js文件中调用钩子。所有的逻辑工作,但我在控制台"React Hook useEffect has a missing dependency: 'initAuth'."
得到一个警告错误,我知道有很多问题,但我不确定这是否与钩子或复杂性有关,我在我的应用程序的高层做。函数来查看我的本地存储并获取我的用户令牌,名称等…我只希望在硬页面刷新,所以它应该只运行一次。
如果我添加initAuth(函数)或authorbject(对象),我得到无限循环。
function App() {
const { initAuth, authObject } = useAuth();
useEffect(() => {
initAuth();
}, []);
// this throws the warning. I need to add dependency
}
如果你只希望这个效果在组件第一次加载时运行一次,那么你可以忽略这个警告。您可以禁用警告,这样它就不会一直显示在控制台中:
useEffect(() => {
initAuth();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
我将如何实现这个钩子:
function App() {
const { initialized, authObject, initAuth } = useAuth();
useEffect(() => {
if (!initialized) {
initAuth();
}
}, [initialized, initAuth]);
...
}
或者更好:
function App() {
const authObject = useAuth(); // let useAuth initialize itself
...
}
通常,useAuth
似乎是一个多用途钩子,被各种组件使用,因此允许多个组件调用initAuth
是没有意义的;钩子应该只返回当前状态。
最好使用上下文
实现该钩子function App() {
return (
<AuthProvider>
<AppContent />
</AuthProvider>
);
}
function AppContent() {
const authObject = useAuth();
...
}
因此,契约转到AuthProvider
,并在状态变化时使用useAuth
通知每个组件。
从OP自己的回答,添加了一些建议的改进:
import React, { createContext, useContext, useState, useMemo } from "react";
const AuthContext = createContext({
isLoggedIn:false /* :Boolean */,
authObject:null /* :Object */,
login: (
username /* :String */,
password /* :String */
) /* :Preomise<Boolean> */ => {
throw new Error('Provider missing');
}
]);
const AuthContextProvider = ({ children }) => {
// init state with function so we do not trigger a
// refresh from useEffect. Use useEffect if the
// initial state is asynchronous
const [state, setState] = useState(() => {
const authObject = localStorage.getItem("authObject");
const isLoggedIn = !!authObject;
return { isLoggedIn, authObject };
});
// avoid refresh if state does not change
const contextValue = useMemo(() => ({
...state, // isLoggedIn, authObject
login: async (username, password) => {
// implement auth protocol, here
// do not expose setState directly in order to
// control what state is actually returned
// setState({ isLoggedIn:..., authObject:... });
// return true|false
}
}), [state]);
return (
<AuthContext.Provider value={ contextValue }>
{ children }
</AuthContext.Provider>
);
};
/**
Usage: const { isLoggedIn, authObject, login } = useAuthContext();
*/
const useAuthContext = () => useContext(AuthContext);
export { useAuthContext, AuthContextProvider };
多亏了Yanick的评论,我就是这样开始向提供商设置我的授权的。我的登录函数为http调用使用了一个认证服务,但是我使用这个上下文函数来正确设置数据
import React, { useContext, useMemo, useState } from "react";
import http from "services/http";
const AuthContext = React.createContext({});
const AuthContextProvider = ({ children }) => {
const [state, setState] = useState(() => {
const authObject = JSON.parse(localStorage.getItem("authObject"));
if (authObject) {
//sets axios default auth header
http.setJwt(authObject.token);
}
const isLoggedIn = !!authObject;
return { isLoggedIn, authObject };
});
// avoid refresh if state does not change
const contextValue = useMemo(
() => ({
...state, // isLoggedIn, authObject
login(auth) {
localStorage.setItem("authObject", JSON.stringify(auth));
http.setJwt(auth.token);
setState({ authObject: auth, isLoggedIn: true });
return true;
},
logout() {
http.setJwt("");
localStorage.removeItem("authObject");
setState({ authObject: null, isLoggedIn: false });
},
}),
[state]
);
return (
<AuthContext.Provider value={contextValue}>{children}</AuthContext.Provider>
);
};
const useAuthContext = () => useContext(AuthContext);
export { useAuthContext, AuthContextProvider };
我的App.js只是使用了ContextProvider,不需要在App.js上运行useEffect。
<AuthContextProvider>
<ThemeProvider theme={darkState ? dark() : light()}>
<CssBaseline>
<BrowserRouter>
//...app.js stuff
</BrowserRouter>
</CssBaseline>
</ThemeProvider>
</AuthContextProvider>
在任何组件中,我现在可以使用如下调用访问isLoggedIn或authorbject:
const { isLoggedIn } = useAuthContext();