代码之家  ›  专栏  ›  技术社区  ›  James

受保护的路由反应路由器4不使用存储在Redux中的身份验证状态

  •  2
  • James  · 技术社区  · 7 年前

    我正在尝试在React Router v4中按照以下步骤创建经过身份验证的路由 example .为后代显示代码:

    function PrivateRoute ({component: Component, authed, ...rest}) {
      return (
        <Route
          {...rest}
          render={(props) => (!!authed)
            ? <Component {...props} />
            : <Redirect to={{pathname: '/login', state: {from: props.location}}} />}
        />
      )
    }
    

    我的身份验证状态( authed ),它在reducer中初始化为空对象,是从Redux存储派生的。这就是我的应用程序。js看起来像:

    class App extends Component {
      componentDidMount() {
        const token = localStorage.getItem("token");
        if (token) {
          this.props.fetchUser();
        }
      }
    
      render() {
        return (
          <Router>
            <div>
              <PrivateRoute authed={this.props.authed} path='/dashboard' component={Dashboard} />
              />
            </div>
          </Router>
        );
      }
    }
    

    问题是 已授权 状态从未定义开始,然后,一旦安装路由器组件,它会将状态更新为 true 。但是,这有点晚了,因为用户已经被重定向回登录页面。我还试图更换 componentDidMount() 生命周期方法 componentWillMount() 但这也没有解决问题。

    你有什么建议?

    更新1:我解决这个问题的唯一方法是测试 已授权 返回之前的状态 <Route /> 此类组件:

      render() {
        if (!!this.props.authed) {
          return (
            <Router>
          <div>
          ...
    

    更新2:我正在使用Redux Thunk中间件来调度操作。状态被正确地作为道具传递-我正在使用 console.log() 中的方法 PrivateRoute 组件来验证状态是否正确变化。当然,问题是它的变化很晚,并且路由已经在重定向用户。

    正在粘贴减速器和动作代码。。。

    措施:

    export const fetchUser = () => async dispatch => {
      dispatch({ type: FETCHING_USER });
      try {
        const res = await axios.get(`${API_URL}/api/current_user`, {
          headers: { authorization: localStorage.getItem("token") }
        });
        dispatch({ type: FETCH_USER, payload: res.data });
      } catch (err) {
        // dispatch error action types
      }
    };
    

    减速器:

    const initialState = {
      authed: {},
      isFetching: false
    };
    ...
        case FETCH_USER: // user authenticated
          return { ...state, isFetching: false, authed: action.payload };
    
    4 回复  |  直到 7 年前
        1
  •  2
  •   Paul Asetre    6 年前

    我也有同样的问题,据我所知,您的更新#1应该是答案。然而,经过进一步调查,我认为这是一个架构问题。专用路由的当前实现取决于同步的信息。

    如果我们务实地考虑 ProtectedRoute 本质上,根据应用程序的状态返回重定向或组件。而不是将每个 Route 使用组件,我们可以将所有路由封装在一个组件中,并从存储中提取信息。

    是的,每个受保护的路由需要编写更多的代码,您需要测试这是否是一个可行的解决方案。

    编辑 :忘记提及另一个架构问题的主要原因是,如果用户刷新受保护的页面,他们将被重定向。

    使现代化 更好的解决方案: 刷新时,如果它们经过身份验证,它将重定向到它们的目标uri https://tylermcginnis.com/react-router-protected-routes-authentication/

    解决方案1

    //You can make this a method instead, that way we don't need to pass authed as an argument
    function Protected(authed, Component, props) {
      return !!authed
        ? <Component {...props} />
        : <Redirect to='/login' />
    }
    
    class AppRouter extends React.PureComponent {
      componentDidMount() {
        const token = localStorage.getItem("token");
        if (token) {
          this.props.fetchUser();
        }
      }
    
      render() {
        let authed = this.props.authed
    
        return (
          <Router>
              <Route path='/protected' render={(props) => Protected(authed, Component, props)} />
          </Router>
        )
      }
    }
    
    class App extends Component {
      render() {
        return (
          <Provider store={store}>
            <AppRouter />
          </Provider>
        )
      }
    }
    

    解决方案2 或者我们可以检查每个组件(是的,这很痛苦)

    class Component extends React.Component {
      render() {
        return (
          !!this.props.authed
            ? <div>...</div>
            : <Redirect to='/' />
        )
      }
    }
    
        2
  •  1
  •   Arnab    6 年前

    同样的问题也发生在我身上,我正在使用一种临时的破解方法来解决它,方法是在内部存储一个加密的值 localStorage 然后在我的 PrivateRoute 组件并检查值是否匹配。

    行动js公司

    localStorage.setItem('isAuthenticated', encryptedText);
    

    私人路线。js公司

    if (localStorage.getItem('isAuthenticated')) {
    const isAuth = decryptedText === my_value;
    return (
         <Route
            {...rest}
            render={(props) =>
               isAuth ? <Component {...props} /> : <Redirect to="/login" />
            }
         />
      );
    } else {
          return <Redirect to="/login" />;
       }
    

    自从 本地存储 速度更快,因此不需要进行不必要的重定向。如果有人删除 本地存储 它们将被重定向到 /login 笔记 :这是临时解决方案。

        3
  •  0
  •   mbarish-me    7 年前

    从理论上讲,您需要从节点API调用中获得承诺,而您现在没有得到承诺。您需要进行架构更改。我建议您使用 redux-promise-middleware 这是一个redux中间件。我有一个样本项目在我的 github 账户如果您拨打此电话,您将得到通知。道具。fetchUser()是否已完成,这取决于您是否可以使用承诺来处理此异步问题。如果需要帮助,请找我。

        4
  •  0
  •   Sander Garretsen    7 年前

    如果您的 App 组件已连接到redux存储,您的意思可能是 this.props.authed 而不是 this.state.authed

    我的身份验证状态(authed),初始化为空 reducer中的对象,派生自Redux存储

    所以您将空对象与 true 在这里: (props) => authed === true ?为什么不用 false ?

    你确定行动 this.props.fetchUser 正在将状态切换到 符合事实的 ?

    也许你最好也发布你的动作和减速器文件