受保护的路由React Router 4无法使用存储在Redux

时间:2018-04-11 14:27:52

标签: reactjs react-redux react-router-v4

我正在尝试按照此example在React Router v4中创建一个经过身份验证的路由。显示后代的代码:

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存储派生的。这就是我的App.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>
    );
  }
}

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

你会建议什么策略?

更新1:解决这个问题的唯一方法是在返回authed组件之前测试<Route />状态,如下所示:

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

更新2:我正在使用Redux Thunk中间件来发送动作。状态正确地作为道具传递 - 我在console.log()组件中使用PrivateRoute方法来验证状态是否正确变异。问题当然是它变异很晚,而Route已经重定向了用户。

粘贴减速器和动作代码......

动作:

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 个答案:

答案 0 :(得分:1)

我遇到了同样的问题,据我了解,您的更新#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='/' />
    )
  }
}

答案 1 :(得分:0)

如果您的App组件已连接到redux商店,则可能表示this.props.authed而不是this.state.authed

  

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

所以你在这里将空对象与true进行比较:(props) => authed === true?为什么不用false初始化它?

您确定行动this.props.fetchUser正在将状态切换为true吗?

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

答案 2 :(得分:0)

理论上,您需要从NODE API调用中获得一个您现在没有获得的承诺。您需要进行体系结构更改。我建议你使用redux-promise-middleware这是一个redux中间件。我的github帐户中有一个示例项目。如果您对this.props.fetchUser()的调用已完成,您将收到通知,根据使用Promise的调用,您可以处理此异步问题。如果需要帮助,请转到那个回购。

答案 3 :(得分:0)

我也遇到了同样的问题,我正在使用临时技巧来解决此问题,方法是将加密的值存储在localStorage内,然后将其解密到我的PrivateRoute组件中,然后检查该值是否匹配。

action.js

localStorage.setItem('isAuthenticated', encryptedText);

PrivateRoute.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" />;
   }

由于localStorage速度更快,因此不必进行不必要的重定向。如果有人删除localStorage,他们将被直接重定向到/login 注意:这是一个临时解决方案。