web-dev-qa-db-ja.com

react-router-v4で非同期を認証する

私はこの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関数はすでに設定されています。

何を表示するかを決定する前に、非同期呼び出しが完了するのをどのように待つことができますか?

12
tommyd456

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>
          )}
        />
      )
    }
  }
}
13
pizza-r0b

@ pizza-r0bのソリューションは私にとって完璧に機能しました。ただし、ロードdivをルートの外側ではなく内側にレンダリングすることで、ロードdivが複数回(アプリ内で定義されたPrivateRouteごとに1回)表示されないように、ソリューションを少し修正する必要がありました( Reactと同様)ルーターの認証例 ):

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>
4
Craig Myles

クラスコンポーネントの代わりにフックを使用した@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} />