0

所以这是我的 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),然后我刷新页面,它总是将我发送回“/”,因为 isAuthenticated 和 isAdmin 的检查是在我的 MainRoutes 组件可以登录用户之前完成的,这当然无法在经过身份验证的选择器中检查auth.js,并将我发送到“/”。我解决这个问题的第一个想法是将这两个标志存储在 localStorage 中,所以即使我的用户没有完成登录,我也会被带到上一个路径。但我想知道是否有任何方法可以专门对 redux 说-auth-wrapper,等到我的 useEffect 函数完成。

谢谢你。

4

2 回答 2

0

这应该保持第一次渲染并让组件有机会验证登录状态,试一试。

但我想知道是否有任何方法可以专门对 redux-auth-wrapper 说,等到我的 useEffect 函数完成。

注意:该解决方案并不特定于 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));
于 2020-04-09T14:00:58.450 回答
0

您可以使用该属性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 */]);

它当然需要您向您的userredux 状态添加一个属性,即。user.isLoading或任何您想命名的名称,默认值为 true。

于 2021-08-20T12:53:11.933 回答