让 redux-auth-wrapper 等待会话检查
Make redux-auth-wrapper wait until session checked
所以这是我的 auth.js 代码:
import locationHelperBuilder from 'redux-auth-wrapper/history4/locationHelper';
import { connectedRouterRedirect } from 'redux-auth-wrapper/history4/redirect';
import { createBrowserHistory } from 'history';
// import Spinner from '../components/layout/Spinner';
const locationHelper = locationHelperBuilder({});
createBrowserHistory();
export const UserIsAdmin = connectedRouterRedirect({
wrapperDisplayName: 'UserIsAdmin',
// AuthenticatingComponent: Spinner,
redirectPath: (state, ownProps) =>
locationHelper.getRedirectQueryParam(ownProps) || '/',
allowRedirectBack: true,
authenticatedSelector: state => state.user.isAuthenticated && state.user.isAdmin
});
export const UserIsAuthenticated = connectedRouterRedirect({
wrapperDisplayName: 'UserIsAuthenticated',
// AuthenticatingComponent: Spinner,
redirectPath: (state, ownProps) =>
locationHelper.getRedirectQueryParam(ownProps) || '/',
allowRedirectBack: true,
authenticatedSelector: state => state.user.isAuthenticated
});
export const UserIsNotAuthenticated = connectedRouterRedirect({
wrapperDisplayName: 'UserIsNotAuthenticated',
// AuthenticatingComponent: Spinner,
redirectPath: (state, ownProps) =>
locationHelper.getRedirectQueryParam(ownProps) || '/',
allowRedirectBack: true,
authenticatedSelector: state => !state.user.isAuthenticated
});
这里是我需要让 redux-auth-wrapper 等待,直到我用用户数据更新状态,以便在刷新页面之前将他发送到他所在的任何地方:
const MainRoutes = ( { cookies } ) => {
// state
const { isAuthenticated } = useSelector( state => state.user );
// dispatch
const dispatch = useDispatch();
const login = () => dispatch( loginAction() );
const logout = () => dispatch( logoutAction() );
// check if session is active ( cookie )
useEffect(() => {
if( !isAuthenticated ) {
const checkSession = async () => {
const cookie = cookies.get('token');
if( cookie && cookie.trim() !== '' ) {
axiosClient.defaults.headers.Authorization = `Bearer ${ cookie }`;
login();
} else logout();
};
checkSession()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ cookies, isAuthenticated ]);
return (
<Switch>
<Route exact path="/" component={ Courses } />
<Route path="/admin" component={ UserIsAdmin( Admin ) } />
<Route path="/profile" component={ UserIsAuthenticated( Profile ) } />
<Route exact path="/login" component={ UserIsNotAuthenticated( Login ) } />
<Route exact path="/signin" component={ UserIsNotAuthenticated( Signin ) } />
<Route exact path="/send-email" component={ UserIsNotAuthenticated( Email ) } />
<Route exact path="/recover" component={ UserIsNotAuthenticated( Recover ) } />
<Route exact path="/policy" component={ Policy } />
<Route exact path="/usage" component={ Usage } />
<Route exact path="/faqs" component={ FAQS } />
</Switch>
);
}
export default withRouter(withCookies(MainRoutes));
基本上我检查是否存在会话 cookie,所以我自动让用户登录。问题是,当我转到某个路由时(例如:/admin,它受到保护,因此受到监督通过 redux-auth.wrapper),我刷新页面,它总是将我发送回 '/',因为在我的 MainRoutes 组件可以让用户登录之前完成了 isAuthenticated 和 isAdmin 的检查,这当然失败了检查 auth.js 的经过身份验证的选择器,并将我发送到“/”。我解决这个问题的第一个想法是将这 2 个标志存储在 localStorage 中,因此即使我的用户没有完成登录,我也会被驱动到以前的路径。但我想知道是否有任何方法可以专门对 redux 说-auth-wrapper,等待我的 useEffect 函数完成。
谢谢。
这应该会进行第一次渲染,让组件有机会验证登录状态,试一试。
But I was wondering if there is any way to specificaly say to redux-auth-wrapper, to wait until my useEffect function has finished.
注意:该解决方案并非特定于 redux-auth-wrapper。
const MainRoutes = ( { cookies } ) => {
const { isAuthenticated } = useSelector( state => state.user );
/* use a state to hold the render */
const [isFirstRender, setFirstRender] = useState(true)
const dispatch = useDispatch();
const login = () => dispatch( loginAction() );
const logout = () => dispatch( logoutAction() );
/* after the first render the user should be logged in (if previously was) */
useEffect(() => {
setFirstRender(false)
}, [])
useEffect(() => {
if( !isAuthenticated ) {
const checkSession = async () => {
const cookie = cookies.get('token');
if( cookie && cookie.trim() !== '' ) {
axiosClient.defaults.headers.Authorization = `Bearer ${ cookie }`;
login();
} else logout();
};
checkSession()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ cookies, isAuthenticated ]);
/* If the effect checking the auth runs fast you can leave
the return as this, otherwise you might want to show a loading
indicator */
return (
<>
{!isFirstRender &&
<Switch>
<Route exact path="/" component={ Courses } />
<Route path="/admin" component={ UserIsAdmin( Admin ) } />
<Route path="/profile" component={ UserIsAuthenticated( Profile ) } />
<Route exact path="/login" component={ UserIsNotAuthenticated( Login ) } />
<Route exact path="/signin" component={ UserIsNotAuthenticated( Signin ) } />
<Route exact path="/send-email" component={ UserIsNotAuthenticated( Email ) } />
<Route exact path="/recover" component={ UserIsNotAuthenticated( Recover ) } />
<Route exact path="/policy" component={ Policy } />
<Route exact path="/usage" component={ Usage } />
<Route exact path="/faqs" component={ FAQS } />
</Switch>
}
</>
);
}
export default withRouter(withCookies(MainRoutes));
您可以使用 属性 authenticatingSelector
推迟重定向,直到您准备好。它告诉你的 connectedRouterRedirect 对象你正在做身份验证的事情,它需要等待它。
const UserIsAdmin = connectedRouterRedirect({
wrapperDisplayName: 'UserIsAdmin',
redirectPath: /* ... */,
allowRedirectBack: true,
authenticatedSelector: state => state.user.isAuthenticated && state.user.isAdmin,
authenticatingSelector: state => state.user.isLoading
});
然后在你的useEffect中:
useEffect(() => {
// You can either use an async here or just bake the dispatch call into your "doSomething" method.
(async () => {
await doSomething();
dispatch(setLoadingStatus({ isLoading: false }));
})();
}, [/* your dependencies here */]);
当然需要您将 属性 添加到 user
redux 状态,即。 user.isLoading
或任何您想命名的名称,其默认值为 true。
所以这是我的 auth.js 代码:
import locationHelperBuilder from 'redux-auth-wrapper/history4/locationHelper';
import { connectedRouterRedirect } from 'redux-auth-wrapper/history4/redirect';
import { createBrowserHistory } from 'history';
// import Spinner from '../components/layout/Spinner';
const locationHelper = locationHelperBuilder({});
createBrowserHistory();
export const UserIsAdmin = connectedRouterRedirect({
wrapperDisplayName: 'UserIsAdmin',
// AuthenticatingComponent: Spinner,
redirectPath: (state, ownProps) =>
locationHelper.getRedirectQueryParam(ownProps) || '/',
allowRedirectBack: true,
authenticatedSelector: state => state.user.isAuthenticated && state.user.isAdmin
});
export const UserIsAuthenticated = connectedRouterRedirect({
wrapperDisplayName: 'UserIsAuthenticated',
// AuthenticatingComponent: Spinner,
redirectPath: (state, ownProps) =>
locationHelper.getRedirectQueryParam(ownProps) || '/',
allowRedirectBack: true,
authenticatedSelector: state => state.user.isAuthenticated
});
export const UserIsNotAuthenticated = connectedRouterRedirect({
wrapperDisplayName: 'UserIsNotAuthenticated',
// AuthenticatingComponent: Spinner,
redirectPath: (state, ownProps) =>
locationHelper.getRedirectQueryParam(ownProps) || '/',
allowRedirectBack: true,
authenticatedSelector: state => !state.user.isAuthenticated
});
这里是我需要让 redux-auth-wrapper 等待,直到我用用户数据更新状态,以便在刷新页面之前将他发送到他所在的任何地方:
const MainRoutes = ( { cookies } ) => {
// state
const { isAuthenticated } = useSelector( state => state.user );
// dispatch
const dispatch = useDispatch();
const login = () => dispatch( loginAction() );
const logout = () => dispatch( logoutAction() );
// check if session is active ( cookie )
useEffect(() => {
if( !isAuthenticated ) {
const checkSession = async () => {
const cookie = cookies.get('token');
if( cookie && cookie.trim() !== '' ) {
axiosClient.defaults.headers.Authorization = `Bearer ${ cookie }`;
login();
} else logout();
};
checkSession()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ cookies, isAuthenticated ]);
return (
<Switch>
<Route exact path="/" component={ Courses } />
<Route path="/admin" component={ UserIsAdmin( Admin ) } />
<Route path="/profile" component={ UserIsAuthenticated( Profile ) } />
<Route exact path="/login" component={ UserIsNotAuthenticated( Login ) } />
<Route exact path="/signin" component={ UserIsNotAuthenticated( Signin ) } />
<Route exact path="/send-email" component={ UserIsNotAuthenticated( Email ) } />
<Route exact path="/recover" component={ UserIsNotAuthenticated( Recover ) } />
<Route exact path="/policy" component={ Policy } />
<Route exact path="/usage" component={ Usage } />
<Route exact path="/faqs" component={ FAQS } />
</Switch>
);
}
export default withRouter(withCookies(MainRoutes));
基本上我检查是否存在会话 cookie,所以我自动让用户登录。问题是,当我转到某个路由时(例如:/admin,它受到保护,因此受到监督通过 redux-auth.wrapper),我刷新页面,它总是将我发送回 '/',因为在我的 MainRoutes 组件可以让用户登录之前完成了 isAuthenticated 和 isAdmin 的检查,这当然失败了检查 auth.js 的经过身份验证的选择器,并将我发送到“/”。我解决这个问题的第一个想法是将这 2 个标志存储在 localStorage 中,因此即使我的用户没有完成登录,我也会被驱动到以前的路径。但我想知道是否有任何方法可以专门对 redux 说-auth-wrapper,等待我的 useEffect 函数完成。
谢谢。
这应该会进行第一次渲染,让组件有机会验证登录状态,试一试。
But I was wondering if there is any way to specificaly say to redux-auth-wrapper, to wait until my useEffect function has finished.
注意:该解决方案并非特定于 redux-auth-wrapper。
const MainRoutes = ( { cookies } ) => {
const { isAuthenticated } = useSelector( state => state.user );
/* use a state to hold the render */
const [isFirstRender, setFirstRender] = useState(true)
const dispatch = useDispatch();
const login = () => dispatch( loginAction() );
const logout = () => dispatch( logoutAction() );
/* after the first render the user should be logged in (if previously was) */
useEffect(() => {
setFirstRender(false)
}, [])
useEffect(() => {
if( !isAuthenticated ) {
const checkSession = async () => {
const cookie = cookies.get('token');
if( cookie && cookie.trim() !== '' ) {
axiosClient.defaults.headers.Authorization = `Bearer ${ cookie }`;
login();
} else logout();
};
checkSession()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ cookies, isAuthenticated ]);
/* If the effect checking the auth runs fast you can leave
the return as this, otherwise you might want to show a loading
indicator */
return (
<>
{!isFirstRender &&
<Switch>
<Route exact path="/" component={ Courses } />
<Route path="/admin" component={ UserIsAdmin( Admin ) } />
<Route path="/profile" component={ UserIsAuthenticated( Profile ) } />
<Route exact path="/login" component={ UserIsNotAuthenticated( Login ) } />
<Route exact path="/signin" component={ UserIsNotAuthenticated( Signin ) } />
<Route exact path="/send-email" component={ UserIsNotAuthenticated( Email ) } />
<Route exact path="/recover" component={ UserIsNotAuthenticated( Recover ) } />
<Route exact path="/policy" component={ Policy } />
<Route exact path="/usage" component={ Usage } />
<Route exact path="/faqs" component={ FAQS } />
</Switch>
}
</>
);
}
export default withRouter(withCookies(MainRoutes));
您可以使用 属性 authenticatingSelector
推迟重定向,直到您准备好。它告诉你的 connectedRouterRedirect 对象你正在做身份验证的事情,它需要等待它。
const UserIsAdmin = connectedRouterRedirect({
wrapperDisplayName: 'UserIsAdmin',
redirectPath: /* ... */,
allowRedirectBack: true,
authenticatedSelector: state => state.user.isAuthenticated && state.user.isAdmin,
authenticatingSelector: state => state.user.isLoading
});
然后在你的useEffect中:
useEffect(() => {
// You can either use an async here or just bake the dispatch call into your "doSomething" method.
(async () => {
await doSomething();
dispatch(setLoadingStatus({ isLoading: false }));
})();
}, [/* your dependencies here */]);
当然需要您将 属性 添加到 user
redux 状态,即。 user.isLoading
或任何您想命名的名称,其默认值为 true。