web-dev-qa-db-ja.com

Reactルーターのプライベートルート/リダイレクトが機能しない

ReduxでNiceを再生するようにプライベートルートのReactルーターの例を少し調整しましたが、他の「ページ」へのリンクまたはリダイレクト時にコンポーネントがレンダリングされません。例はここにあります:

https://reacttraining.com/react-router/web/example/auth-workflow

PrivateRouteコンポーネントは次のようになります。

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

しかし、Reduxアプリケーションに組み込んだため、Reduxストアとルートの小道具にアクセスできるようにPrivateRouteを少し調整する必要がありました。

const PrivateRouteComponent = (props) => (
    <Route {...props.routeProps} render={() => (
    props.logged_in ? (
        <div>{props.children}</div>
        ) : (
        <Redirect to={{
            pathname: '/login',
            state: { from: props.location }
        }} /> )
    )} />
);

const mapStateToProps = (state, ownProps) => {
    return {
        logged_in: state.auth.logged_in,
        location: ownProps.path,
        routeProps: {
            exact: ownProps.exact,
            path: ownProps.path
        }
    };
};

const PrivateRoute = connect(mapStateToProps, null)(PrivateRouteComponent);
export default PrivateRoute

ログインせずにPrivateRouteにアクセスすると、/ loginに正しくリダイレ​​クトされます。ただし、たとえばログインして<Redirect .../>を使用するか、<Link ...>をPrivateRouteにクリックすると、URIは更新されますが、ビューは更新されません。同じコンポーネントに残ります。

私は何を間違えていますか?


写真を完成させるために、アプリのindex.jsには無関係なものがいくつかあり、ルートは次のように設定されています。

ReactDOM.render(
    <Provider store={store}>
        <App>
            <Router>
                <div>
                    <PrivateRoute exact path="/"><Home /></PrivateRoute>
                    // ... other private routes
                    <Route path="/login" component={Login} />
                </div>
            </Router>
        </App>
    </Provider>,
    document.getElementById('root')
);
33
Rein

Route<Switch>タグでラップする必要があります

ReactDOM.render(
<Provider store={store}>
    <App>
        <Router>
            <div>
                <Switch>
                   <PrivateRoute exact path="/"><Home /></PrivateRoute>
                   // ... other private routes
                   <Route path="/login" component={Login} />
                </Switch>
            </div>
        </Router>
    </App>
</Provider>,
document.getElementById('root'));
11
Eli

プライベートルートを純粋ではないように設定します。

export default connect(mapStateToProps, null, null, {
  pure: false,
})(PrivateRoute);

これにより、コンポーネントが再レンダリングされます。

react-router-4-x-privateroute-not-working-after-connecting-to-redux をご覧ください。

8
worker11811

同じ問題があったので、App reduxコンテナを作成し、isAuthenticatedをPrivateRouteにプロップとして渡すことで解決しました

ここにある、私はそれが役立つことを願っています

const App = (props) => {
return (
  <Provider store={store}>
    <Router>
      <div>
        <PrivateRoute path="/secured" component={Secured} isAuthenticated={props.isAuthenticated} />
      </div>
    </Router>
  </Provider>
  );
};

const mapStateToProps = state => ({
  isAuthenticated: state.isAuthenticated
});

export default connect(mapStateToProps)(App);

その後、PrivateRouteで

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

export default PrivateRoute;
6
Danijel

restパラメーターを使用してmapStateToPropsのデータにアクセスすることで、これを機能させることができました。

const PrivateRoute = ({component: Component, ...rest}) => {
  const {isAuthenticated} = rest;

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

PrivateRoute.propTypes = {
  isAuthenticated: PropTypes.bool.isRequired,
};

function mapStateToProps(state) {
  return {
    isAuthenticated: state.user.isAuthenticated,
  };
}

export default connect(mapStateToProps)(PrivateRoute);
5
Newm

さて、この質問への答えはもっと詳細にすべきだと思うので、ここで私は4時間掘りました。

コンポーネントをconnect()でラップすると、React ReduxはshouldComponentUpdate(githubの問題に関する回答を検索する場合はsCU)を実装し、propsで浅い比較を行います(propsオブジェクトの各キーを介して確認します)値が「===」と同一の場合)。実際に意味するのは、コンポーネントがPureとみなされることです。 今では、小道具が変更されたときのみ変更されます!これがここの重要なメッセージです。 2番目の重要なメッセージであるReactルーターは、コンテキストを使用して、ルーターからルートコンポーネントに場所、一致、および履歴オブジェクトを渡します。 propsは使用しません。

さて、実際に何が起こるか見てみましょう。それを知っていても、まだかなりトリッキーです。

  • ケース1

接続後の小道具には3つのキーがあります:パス、コンポーネント、および認証(接続によって指定されます)。そのため、実際には、ラップされたコンポーネントは気にしないので、ルートの変更時にまったく再レンダリングされません。ルートが変更されても、小道具は変更されず、更新されません。

  • ケース3

接続後、propsには4つのキーがあります:path、component、auth、anyprop。ここでのコツは、anypropがコンポーネントが呼び出されるたびに作成されるオブジェクトであることです。したがって、コンポーネントが呼び出されるたびに、この比較が行われます:{a:1} === {a:1}、これは(試してみてください)falseになるため、コンポーネントは毎回更新されます。ただし、コンポーネントはルートを気にしませんが、その子は気にしません。

  • ケース2

Appファイルでこの行を呼び出すと推測されるため、ここに謎があります。そこには「auth」への参照はないはずであり、エラーが発生するはずです(少なくともそれは私が推測していることです)。私の推測では、Appファイルの「auth」は、そこに定義されているオブジェクトを参照しています。

次に何をすべきか?

ここには2つのオプションがあります。

  1. コンポーネントが純粋ではないことをReact Reduxに伝えます。これにより、sCUインジェクションが削除され、コンポーネントが正しく更新されるようになります。

    connect(mapStateToProps、null、null、{pure:false})(PrivateRoute)

  2. WithRouter()を使用します。これにより、場所、一致、および履歴オブジェクトが小道具としてコンポーネントに注入されます。今、私は内部を知りませんが、Reactルーターはそれらのオブジェクトを変更しないので、ルートが変わるたびにその小道具も変わり(sCUはtrueを返します)、コンポーネントは正しく更新されます。これの副作用は、あなたのReactツリーが多くのWithRouterとRouteで汚染されていることです...

Githubの問題への参照: Dealing with Update Blocking

ここで、withRouterはクイックフィックスとして意図されているが、推奨される解決策ではないことがわかります。 pure:falseの使用は言及されていないため、この修正がどれほど良いかはわかりません。

3番目の解決策を見つけましたが、Higher Order Componentを使用してwithRouterよりも本当に良い解決策であるかどうかはわかりませんが高次コンポーネントをReduxストアに接続すると、ルートがレンダリングするものを気にしなくなり、HOCがそれを処理します。

import Notlogged from "./Notlogged";    
function Isloggedhoc({ wrap: Component, islogged, ...props }) {
      return islogged ? <Component {...props} /> : <Notlogged {...props} />;
    }

const mapState = (state, ownprops) => ({
      islogged: state.logged,
      ...ownprops
    });

export default connect(mapState)(Isloggedhoc);

app.jsで

<Route path="/PrivateRoute" render={props => <Isloadedhoc wrap={Mycomponent} />} />

カリー化された関数を作成して、少し短くすることもできます。

function isLogged(component) {
  return function(props) {
    return <Isloadedhoc wrap={component} {...props} />;
  };
}

そのように使用する:

<Route path="/PrivateRoute" render={isLogged(Mycomponent)} />
4
TheoH

私もこの問題に苦労していますが、ここに私の解決策があります。

すべての<PrivateRoute>コンポーネントにisAuthenticatedを渡す代わりに、<PrivateRoute>自体の状態からisAuthenticatedを取得するだけです。

import React from 'react';
import {Route, Redirect, withRouter} from 'react-router-dom';
import {connect} from 'react-redux';

// isAuthenticated is passed as prop here
const PrivateRoute = ({component: Component, isAuthenticated , ...rest}) => {
    return <Route
        {...rest}
        render={
            props => {
                return isAuthenticated ?
                    (
                        <Component {...props} />
                    )
                    :
                    (
                        <Redirect
                            to={{
                                pathname: "/login",
                                state: {from: props.location}
                            }}
                        />
                    )
            }
        }
    />
};

const mapStateToProps = state => (
    {
        // isAuthenticated  value is get from here
        isAuthenticated : state.auth.isAuthenticated 
    }
);

export default withRouter(connect(
    mapStateToProps, null, null, {pure: false}
)(PrivateRoute));
3
Hoang Trinh

react-router documentation によると、単にconnect関数をwithRouterでラップできます:

// before
export default connect(mapStateToProps)(Something)

// after
import { withRouter } from 'react-router-dom'
export default withRouter(connect(mapStateToProps)(Something))

これは私にとってはうまくいき、この場合はルートとともにビューが更新され始めました。

1

@Reinのような同様の問題があります。私の場合、PrivateRouteは元のバージョンとほぼ同じように見えますが、元の例ではReduxにのみ接続され、fakeAuthの代わりにそれを使用しました。

const PrivateRoute = ({ component: Component, auth, ...rest }) => (
  <Route
   {...rest}
   render={props =>
   auth.isAuthenticated
    ? <Component {...props} />
    : <Redirect to={{ pathname: "/login" }} />}
  />
);

 PrivateRoute.propTypes = {
  auth: PropTypes.object.isRequired,
  component: PropTypes.func.isRequired
 }

 const mapStateToProps = (state, ownProps) => {
  return {
     auth: state.auth
  }
};

export default connect(mapStateToProps)(PrivateRoute);

使用方法と結果:-

  1. 動作していませんが、期待しています動作します
    • <PrivateRoute path="/member" component={MemberPage} />
  2. 動作しますが、このように使用することは望ましくありません
    • <PrivateRoute path="/member" component={MemberPage} auth={auth} />
  3. ワーキング。動作するだけで、まったく使用したくない。この点からの理解は、元のPrivateRouteをReduxに接続する場合、PrivateRouteが機能するように追加のプロップ(任意のプロップ)を渡す必要があるということです。 誰でも、この振る舞いに関するヒントを教えてください。これが私の主な関心事です。 新しい質問として
    • <PrivateRoute path="/member" component={MemberPage} anyprop={{a:1}} />
0