使用 react-router-v4 验证异步

IT技术 reactjs react-router react-router-v4
2021-04-13 01:48:31

我有这个PrivateRoute组件(来自文档):

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

我想更改isAuthenticated为 aysnc 请求isAuthenticated()但是,在响应返回之前页面重定向。

为了澄清,该isAuthenticated功能已经设置。

在决定显示什么之前,如何等待异步调用完成?

3个回答

如果您不使用 Redux 或任何其他类型的状态管理模式,您可以使用Redirect组件和组件状态来确定页面是否应呈现。这将包括将状态设置为加载状态,进行异步调用,在请求完成后保存用户,或者缺少用户来声明和呈现Redirect组件,如果不满足条件,组件将重定向。

class PrivateRoute extends React.Component {
  state = {
    loading: true,
    isAuthenticated: false,
  }
  componentDidMount() {
    asyncCall().then((isAuthenticated) => {
      this.setState({
        loading: false,
        isAuthenticated,
      });
    });
  }
  render() {
    const { component: Component, ...rest } = this.props;
    if (this.state.loading) {
      return <div>LOADING</div>;
    } else {
      return (
        <Route {...rest} render={props => (
          <div>
            {!this.state.isAuthenticated && <Redirect to={{ pathname: '/login', state: { from: this.props.location } }} />}
            <Component {...this.props} />
          </div>
          )}
        />
      )
    }
  }
}
@pizza-r0b componentDidMount() 方法只调用一次。它仅在用户单击第一个链接时调用。它不满足条件,即每当用户访问私有路由时,componentDidMount 或包含 promise 的方法应调用。我怎样才能实现它?
2021-05-24 01:48:31
您需要配置 babel 以使用类属性转换,或者使用 babel 预设阶段 0 - 2 才能工作@raddevon
2021-05-27 01:48:31
@J.Pedro 本质上客户端 JS 是不安全的。要将 isAuthenticated 变量更改为 true 需要的不仅仅是打开开发控制台——您可能必须筛选已编译、缩小的代码,然后代理一个新的 JS 文件。如果有人有足够的决心这样做,他们将能够看到一些isAuthenticatedUI,但是后端负责显示所有经过身份验证的数据并验证用户是否经过身份验证并允许查看数据。因此,即使有人看到经过身份验证的 UI - 保护敏感数据也取决于后端。
2021-06-04 01:48:31
@pizza-r0b 你能告诉我 index.js 路由器设置对你的代码有什么要求吗?
2021-06-10 01:48:31
state = …在类声明中对我不起作用。我不得不使用构造函数来设置初始状态。
2021-06-21 01:48:31

如果有人对使用钩子而不是类组件的@CraigMyles 实现感兴趣:

export const PrivateRoute = (props) => {
    const [loading, setLoading] = useState(true);
    const [isAuthenticated, setIsAuthenticated] = useState(false);

    const { component: Component, ...rest } = props;

    useEffect(() => {
        const fetchData = async () => {
            const result = await asynCall();

            setIsAuthenticated(result);
            setLoading(false);
        };
        fetchData();
    }, []);

    return (
        <Route
            {...rest}
            render={() =>
                isAuthenticated ? (
                    <Component {...props} />
                ) : loading ? (
                    <div>LOADING...</div>
                ) : (
                    <Redirect
                        to={{
                            pathname: "/login",
                            state: { from: props.location },
                        }}
                    />
                )
            }
        />
    );
};


使用以下方法调用时效果很好:

<PrivateRoute path="/routeA" component={ComponentA} />
<PrivateRoute path="/routeB" component={ComponentB} />
为什么需要有一个跟踪加载的状态?是否可以修改三元语句以仅考虑身份验证状态变量?
2021-05-27 01:48:31
完全忘记了第二个参数,这是必须的!为了有一个单独的“加载”的必要性,因为“isAuthenticated”,我想你可以从最后一个 undefined 开始,然后从那里开始,这是一种有效的方法!谢谢你的提示
2021-05-27 01:48:31
此方法仅呈现加载屏幕,但在异步调用结束后不会更新,它只是停留在加载屏幕中,我该怎么办?
2021-06-09 01:48:31
我相信您可以通过[setIsAuthenticated]useEffect().
2021-06-15 01:48:31

@pizza-r0b 的解决方案对我来说非常有效。但是,我不得不稍微修改解决方案,以防止加载 div 被多次显示(对于应用程序中定义的每个 PrivateRoute 一次),通过在 Route 内部而不是外部渲染加载 div(类似于React Router 的 auth 示例) :

class PrivateRoute extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      loading: true,
      isAuthenticated: false
    }
  }

  componentDidMount() {
    asyncCall().then((isAuthenticated) => {
      this.setState({
        loading: false,
        isAuthenticated
      })
    })
  }

  render() {
    const { component: Component, ...rest } = this.props
    return (
      <Route
        {...rest}
        render={props =>
          this.state.isAuthenticated ? (
            <Component {...props} />
          ) : (
              this.state.loading ? (
                <div>LOADING</div>
              ) : (
                  <Redirect to={{ pathname: '/login', state: { from: this.props.location } }} />
                )
            )
        }
      />
    )
  }
}

为了完整起见,从我的 App.js 中摘录:

<DashboardLayout>
  <PrivateRoute exact path="/status" component={Status} />
  <PrivateRoute exact path="/account" component={Account} />
</DashboardLayout>
@Annjawn 您不会将/login路线定义为 a PrivateRoute,而是将其定义普通的Route. 登录页面不是私密的。
2021-06-01 01:48:31
嘿 Craig,这确实有效,但你能告诉我/login将被路由到哪里吗?我的意思是哪个组件?这应该是一个“登录”组件的地方,但如果我定义/login喜欢<PrivateRoute exact path="/login" component={Login} />在App.js这将是一个无限循环。
2021-06-03 01:48:31