您如何等待 useAuth useEffect 到 return 当前用户状态?
How do you wait for useAuth useEffect to return the current user state?
我在为我的 React 应用程序实施身份验证时遇到了一些问题。我按照 this link 进行身份验证。这是我的 App 组件:
function App() {
return (
<ProvideAuth>
<BrowserRouter>
<Header />
<Switch>
<PrivateRoute exact path="/">
<Dashboard />
</PrivateRoute>
<Route path="/login">
<Login />
</Route>
</Switch>
</BrowserRouter>
</ProvideAuth>
);
}
function PrivateRoute({ children, ...rest }) {
let auth = useAuth();
console.log("USER: ", auth.user);
return (
<Route
{...rest}
render={({ location }) =>
auth.user ? (
children
) : (
<Redirect
to={{
pathname: "/login",
state: { from: location }
}}
/>
)} />
)
}
export default App;
登录组件:
const Login = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
let history = useHistory();
let location = useLocation();
let auth = useAuth();
let { from } = location.state || { from: { pathname: "/" } }
let login = (e) => {
auth.signin(email, password, () => {
history.replace(from);
});
};
return (
<div>
<input onChange={e => setEmail(e.target.value)} value={email} type="email" />
<input onChange={e => setPassword(e.target.value)} value={password} type="password" />
</div>
)
}
export default Login;
终于使用-auth.js:
const authContext = createContext();
export function ProvideAuth({ children }) {
const auth = useProvideAuth();
return <authContext.Provider value={auth}>{children}</authContext.Provider>;
};
export const useAuth = () => {
return useContext(authContext);
};
function useProvideAuth() {
const [user, setUser] = useState(null);
const signin = (email, password, callback) => {
axios.post(`${apiUrl}/sign_in`, {
'email': email,
'password': password
},
{
headers: {
'Content-Type': 'application/json'
}
}).then(res => {
const expiryDate = new Date(new Date().getTime() + 6 * 60 * 60 * 1000).toUTCString();
document.cookie = `access-token=${res.headers['access-token']}; path=/; expires=${expiryDate}; secure; samesite=lax`;
return res.data
})
.then(data => {
setUser(data.data);
callback();
})
.catch(e => {
setUser(null);
});
};
const signout = () => {
document.cookie = "access-token=; expires = Thu, 01 Jan 1970 00:00:00 GMT";
setUser(null);
}
useEffect(() => {
const cookies = getCookies();
if (cookies['access-token']) {
axios.get(`${apiUrl}/user_info`, {
headers: {
...cookies
}
}).then(res => {
return res.data;
})
.then(data => {
setUser(data);
})
.catch(e => {
setUser(null);
})
} else {
setUser(null);
}
}, []);
return {
user,
signin,
signout
}
}
function getCookies() {
let cookies = document.cookie.split(';');
let authTokens = {
'access-token': null
};
for (const cookie of cookies) {
let cookiePair = cookie.split('=');
if (authTokens.hasOwnProperty(cookiePair[0].trim().toLowerCase()))
authTokens[cookiePair[0].trim()] = decodeURIComponent(cookiePair[1]);
}
return authTokens;
}
然后仪表板组件就是主页。没什么意思。
问题是当用户实际登录时(设置访问令牌 cookie 以及其他令牌),他们仍然被路由到登录页面,因为调用 API 检查这些令牌是否有效是异步的,因此用户最初设置为 null。
我在这里错过了什么?如何在不阻塞用户界面的情况下等待 API 响应返回?我应该将用户状态保存在 redux 状态还是有其他解决方法?
非常感谢!
按照 Jonas Wilms 的建议,我在 user-auth 中添加了一个类似于 user 的加载状态变量,并在每次请求前将其设置为 true,在请求完成后将其设置为 false。
在我的 App 组件中,我更改了 PrivateRoute 函数以在用户状态加载时显示加载微调器。当它设置为 false 时,我会检查用户是否登录并相应地显示 Dashboard 组件或重定向到登录页面。
function PrivateRoute({ children, ...rest }) {
let auth = useAuth();
return (
<Route
{...rest}
render={({ location }) =>
auth.loading ?
<Loading /> :
auth.user ? (
children
) : (
<Redirect
to={{
pathname: "/login",
state: { from: location }
}}
/>
)} />
)
}
我在为我的 React 应用程序实施身份验证时遇到了一些问题。我按照 this link 进行身份验证。这是我的 App 组件:
function App() {
return (
<ProvideAuth>
<BrowserRouter>
<Header />
<Switch>
<PrivateRoute exact path="/">
<Dashboard />
</PrivateRoute>
<Route path="/login">
<Login />
</Route>
</Switch>
</BrowserRouter>
</ProvideAuth>
);
}
function PrivateRoute({ children, ...rest }) {
let auth = useAuth();
console.log("USER: ", auth.user);
return (
<Route
{...rest}
render={({ location }) =>
auth.user ? (
children
) : (
<Redirect
to={{
pathname: "/login",
state: { from: location }
}}
/>
)} />
)
}
export default App;
登录组件:
const Login = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
let history = useHistory();
let location = useLocation();
let auth = useAuth();
let { from } = location.state || { from: { pathname: "/" } }
let login = (e) => {
auth.signin(email, password, () => {
history.replace(from);
});
};
return (
<div>
<input onChange={e => setEmail(e.target.value)} value={email} type="email" />
<input onChange={e => setPassword(e.target.value)} value={password} type="password" />
</div>
)
}
export default Login;
终于使用-auth.js:
const authContext = createContext();
export function ProvideAuth({ children }) {
const auth = useProvideAuth();
return <authContext.Provider value={auth}>{children}</authContext.Provider>;
};
export const useAuth = () => {
return useContext(authContext);
};
function useProvideAuth() {
const [user, setUser] = useState(null);
const signin = (email, password, callback) => {
axios.post(`${apiUrl}/sign_in`, {
'email': email,
'password': password
},
{
headers: {
'Content-Type': 'application/json'
}
}).then(res => {
const expiryDate = new Date(new Date().getTime() + 6 * 60 * 60 * 1000).toUTCString();
document.cookie = `access-token=${res.headers['access-token']}; path=/; expires=${expiryDate}; secure; samesite=lax`;
return res.data
})
.then(data => {
setUser(data.data);
callback();
})
.catch(e => {
setUser(null);
});
};
const signout = () => {
document.cookie = "access-token=; expires = Thu, 01 Jan 1970 00:00:00 GMT";
setUser(null);
}
useEffect(() => {
const cookies = getCookies();
if (cookies['access-token']) {
axios.get(`${apiUrl}/user_info`, {
headers: {
...cookies
}
}).then(res => {
return res.data;
})
.then(data => {
setUser(data);
})
.catch(e => {
setUser(null);
})
} else {
setUser(null);
}
}, []);
return {
user,
signin,
signout
}
}
function getCookies() {
let cookies = document.cookie.split(';');
let authTokens = {
'access-token': null
};
for (const cookie of cookies) {
let cookiePair = cookie.split('=');
if (authTokens.hasOwnProperty(cookiePair[0].trim().toLowerCase()))
authTokens[cookiePair[0].trim()] = decodeURIComponent(cookiePair[1]);
}
return authTokens;
}
然后仪表板组件就是主页。没什么意思。
问题是当用户实际登录时(设置访问令牌 cookie 以及其他令牌),他们仍然被路由到登录页面,因为调用 API 检查这些令牌是否有效是异步的,因此用户最初设置为 null。
我在这里错过了什么?如何在不阻塞用户界面的情况下等待 API 响应返回?我应该将用户状态保存在 redux 状态还是有其他解决方法?
非常感谢!
按照 Jonas Wilms 的建议,我在 user-auth 中添加了一个类似于 user 的加载状态变量,并在每次请求前将其设置为 true,在请求完成后将其设置为 false。
在我的 App 组件中,我更改了 PrivateRoute 函数以在用户状态加载时显示加载微调器。当它设置为 false 时,我会检查用户是否登录并相应地显示 Dashboard 组件或重定向到登录页面。
function PrivateRoute({ children, ...rest }) {
let auth = useAuth();
return (
<Route
{...rest}
render={({ location }) =>
auth.loading ?
<Loading /> :
auth.user ? (
children
) : (
<Redirect
to={{
pathname: "/login",
state: { from: location }
}}
/>
)} />
)
}