まえがき
v4 です。
困った!
ページで componentWillReceiveProps
でやればよいはずの処理が走らず、なぜか毎回 componentWillMount
が走っていた。つまり、component が update (re-render) するのではなく mount (re-mount) をしてた。なんでだろうな〜と思ったら、そのページはログイン済みの(authenticated な)ユーザーにのみ表示するページで、 PrivateRoute
(後述)を使用していた。react-router の公式ドキュメントをちゃんと読んでごにょごにょしたら解決策がわかった。
公式のやりかた
公式ドキュメント はこんな風にやろうって言っている。
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={props =>
fakeAuth.isAuthenticated ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
/>
);
こんな感じで Route
をいい感じに wrap して PrivateRoute
として使う。こうすると、 isAuthenticated
でないときには勝手にログインページへ redirect してくれる。使うときは Route
を使うときと同じ要領で、 <PrivateRoute path="/mypage" component={Mypage} />
って感じにすればよい。
でも問題がある
僕らは Route
にページコンポーネントを渡すとき、 component
は使いたくない。なぜなら、
When you use component (instead of render or children, below) the router uses React.createElement to create a new React element from the given component. That means if you provide an inline function to the component prop, you would create a new component every render. This results in the existing component unmounting and the new component mounting instead of just updating the existing component. When using an inline function for inline rendering, use the render or the children prop (below).
(拙訳)
これを使うと、内部では毎回React.createElement
をやるので、レンダリング毎に新しい React element を生成しちゃいます。つまり、ページコンポーネントの update 処理をするのではなく、component の unmount & mount をしまくる、ってことになります。これが嫌だったらRoute
にページコンポーネントを渡すときcomponent
じゃなくてrender
とかchildren
とかを props で使おう。
src: https://reacttraining.com/react-router/web/api/Route/component
らしいから。
参考になる:https://qiita.com/park-jh/items/7486a0d1dcc5f32c69ec#render-props%E3%82%92%E4%BD%BF%E3%81%86
どうやるか
前述の PrivateRoute
は、Route
で component
props を使う場合のやつなので、 render
props を使うバージョンにする。
const PrivateRoute = ({ render, ...rest }) => (
<Route
{...rest}
render={
fakeAuth.isAuthenticated
? render
: props => (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
/>
);
使いかたは他の Route
と同様、 <PrivateRoute path="mypage" render={props => <Mypage {...props} />} />
でオーケー。
あとがき
簡単ですね。