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')
);
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'));
プライベートルートを純粋ではないように設定します。
export default connect(mapStateToProps, null, null, {
pure: false,
})(PrivateRoute);
これにより、コンポーネントが再レンダリングされます。
react-router-4-x-privateroute-not-working-after-connecting-to-redux をご覧ください。
同じ問題があったので、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;
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);
さて、この質問への答えはもっと詳細にすべきだと思うので、ここで私は4時間掘りました。
コンポーネントをconnect()でラップすると、React ReduxはshouldComponentUpdate(githubの問題に関する回答を検索する場合はsCU)を実装し、propsで浅い比較を行います(propsオブジェクトの各キーを介して確認します)値が「===」と同一の場合)。実際に意味するのは、コンポーネントがPureとみなされることです。 今では、小道具が変更されたときのみ変更されます!これがここの重要なメッセージです。 2番目の重要なメッセージであるReactルーターは、コンテキストを使用して、ルーターからルートコンポーネントに場所、一致、および履歴オブジェクトを渡します。 propsは使用しません。
さて、実際に何が起こるか見てみましょう。それを知っていても、まだかなりトリッキーです。
接続後の小道具には3つのキーがあります:パス、コンポーネント、および認証(接続によって指定されます)。そのため、実際には、ラップされたコンポーネントは気にしないので、ルートの変更時にまったく再レンダリングされません。ルートが変更されても、小道具は変更されず、更新されません。
接続後、propsには4つのキーがあります:path、component、auth、anyprop。ここでのコツは、anypropがコンポーネントが呼び出されるたびに作成されるオブジェクトであることです。したがって、コンポーネントが呼び出されるたびに、この比較が行われます:{a:1} === {a:1}、これは(試してみてください)falseになるため、コンポーネントは毎回更新されます。ただし、コンポーネントはルートを気にしませんが、その子は気にしません。
Appファイルでこの行を呼び出すと推測されるため、ここに謎があります。そこには「auth」への参照はないはずであり、エラーが発生するはずです(少なくともそれは私が推測していることです)。私の推測では、Appファイルの「auth」は、そこに定義されているオブジェクトを参照しています。
次に何をすべきか?
ここには2つのオプションがあります。
コンポーネントが純粋ではないことをReact Reduxに伝えます。これにより、sCUインジェクションが削除され、コンポーネントが正しく更新されるようになります。
connect(mapStateToProps、null、null、{pure:false})(PrivateRoute)
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)} />
私もこの問題に苦労していますが、ここに私の解決策があります。
すべての<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));
react-router documentation によると、単にconnect
関数をwithRouter
でラップできます:
// before
export default connect(mapStateToProps)(Something)
// after
import { withRouter } from 'react-router-dom'
export default withRouter(connect(mapStateToProps)(Something))
これは私にとってはうまくいき、この場合はルートとともにビューが更新され始めました。
@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);
使用方法と結果:-
<PrivateRoute path="/member" component={MemberPage} />
<PrivateRoute path="/member" component={MemberPage} auth={auth} />
<PrivateRoute path="/member" component={MemberPage} anyprop={{a:1}} />