react-router v4にてchild componentにpropsを渡す方法


react-router v4にてchild componentにpropsを渡す方法には２つがある。

* render propsを使う。
* recomposeでhigher order component(HOC)を作る。

## render propsを使う。

Routeをレンダリングする方法には３つがある。

* &lt;Route component&gt;
* &lt;Route render&gt;
* &lt;Route children&gt;

マッチされたurlに対して、指定されたcomponentをレンダリングするのがRoute componentである。
Route renderはinlineで指定されたのをレンダリングする。
Route componentとは違ってレンダリングの対象にpropertyを指定して渡すのができる。
ここではRoute renderを用いてchild componentにpropertyを渡してみよう。（Route childrenについては割愛する。）

次の記事にあるソースコードをそのまま使って直した。

* [サンプルでreact-router v4を理解してみよう。](http://qiita.com/park-jh/items/b4c7b16ea9eb0cf44942#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB)

```diff:src/components/routing/Main.js
--- a/src/components/routing/Main.js
+++ b/src/components/routing/Main.js
@@ -8,7 +8,7 @@ const Main = () => (
   <main>
     <Switch>
       <Route exact path="/" component={Home} />
-      <Route path="/about" component={About} />
+      <Route path="/about" render={() => <About title="About Us" />} />
       <Route path="/repos" component={Repos} />
     </Switch>
   </main>
```

ここではAboutというpure component(=stateless component)のpropsにtitleを追加して渡してる。

```diff:src/components/routing/About.js
--- a/src/components/routing/About.js
+++ b/src/components/routing/About.js
@@ -1,11 +1,16 @@
 import React, { Component } from 'react';
+import PropTypes from 'prop-types';

 class About extends Component {
   render() {
     return (
-      <h1>About</h1>
+      <h1>{this.props.title}</h1>
     );
   }
 }

+About.propTypes = {
+  title: PropTypes.string.isRequired,
+};
+
 export default About;
```

Aboutリンクをクリックすると、"About"の代わりに"About Us"が表示される。

## recomposeでhigher order component(HOC)を作る。

### higher order componentとは

higher orderは「上位」又は「高位」という意味らしい。
既存のcomponentより高位のcomponentという意味で名付けたことではないだろうか。
high order componentは、react componentをレンダリングするfunctionを返すfunctionを言う。
既存のcomponentに新たな機能を追加して再組み立て(recompose)したものだと考えてもいい。
このとき、よく使われるlibraryがrecomposeである。
recomposeのwithPropというmoduleを使えば、既存のcomponentにpropsの追加が容易である。
recomposeにはwithProp以外にもいろんなmoduleが存在する。
ここではwithPropだけを紹介するが、reactにもっと上達になりたい場合は[recomposeのgithub](https://github.com/acdlite/recompose)を参照しよう。

さあ、recomposeを設置しよう。

```sh
  yarn add recompose
```

既存のサンプルからpure component(=stateless component)をarrow functionで直してみた。
pure componentの場合は、class式よりfunctionで作るのがもっといい方法らしい。
[Building HOCs with Recompose](https://medium.com/front-end-developers/building-hocs-with-recompose-7debb951d101)の記事になぜpure componentはfunctionで作るべきなのかについて書かれていたので紹介する。

> Modular code : Reusable pieces that can plug in across your site
> Reliance on a props-only interface : Stateless interface by default
> Easily Unit Tested : Easily test interface with enzyme/jest
> Easily Mocked : Easily mock props for different situations

pure componentはstatelessなviewのpieceでサイト内いろんなところに跨って使われる。
そして、テストコードやmockを作る場合、functionのほうがもっと容易である。
コードを組むときはテストしやすい形で組むのが正しい。

```diff:src/components/routing/About.js
--- a/src/components/routing/About.js
+++ b/src/components/routing/About.js
@@ -1,13 +1,9 @@
-import React, { Component } from 'react';
+import React from 'react';
 import PropTypes from 'prop-types';

-class About extends Component {
-  render() {
-    return (
-      <h1>{this.props.title}</h1>
-    );
-  }
-}
+const About = props => (
+  <h1>{props.title}</h1>
+);

 About.propTypes = {
   title: PropTypes.string.isRequired,
```

```diff:src/components/routing/Home.js
--- a/src/components/routing/Home.js
+++ b/src/components/routing/Home.js
@@ -1,11 +1,7 @@
-import React, { Component } from 'react';
+import React from 'react';

-class Home extends Component {
-  render() {
-    return (
-      <h1>HOME</h1>
-    );
-  }
-}
+const Home = () => (
+  <h1>HOME</h1>
+);

 export default Home;
```

次が肝心のところだ。
recomposeのwithPropsを用いてAbout componentを再組み立て（recompose）する。
Aboutのpropsにtitleを追加し、AboutをラップしたAboutWithTitleを作った。

```diff:src/components/routing/Main.js
--- a/src/components/routing/Main.js
+++ b/src/components/routing/Main.js
@@ -1,17 +1,26 @@
 import React from 'react';
 import { Switch, Route } from 'react-router-dom';
+import withProps from 'recompose/withProps';
 import About from './About';
 import Home from './Home';
 import Repos from './Repos';

-const Main = () => (
-  <main>
-    <Switch>
-      <Route exact path="/" component={Home} />
-      <Route path="/about" render={() => <About title="About Us" />} />
-      <Route path="/repos" component={Repos} />
-    </Switch>
-  </main>
-);
+const Main = () => {
+  const AboutWithTitle = withProps(
+    props => ({
+      ...props,
+      title: 'About Us',
+    }),
+  )(About);
+  return (
+    <main>
+      <Switch>
+        <Route exact path="/" component={Home} />
+        <Route path="/about" component={AboutWithTitle} />
+        <Route path="/repos" component={Repos} />
+      </Switch>
+    </main>
+  );
+};

 export default Main;
```

これでAbout componentにtitle propertyが注入された。
Aboutリンクをクリックすると、"About"の代わりに"About Us"が表示される。

## 参考

* [You Can Pass Props To Route Components](https://codeburst.io/react-router-v4-unofficial-migration-guide-5a370b8905a)
* [Recompose](https://github.com/acdlite/recompose)
* [Building HOCs with Recompose](https://medium.com/front-end-developers/building-hocs-with-recompose-7debb951d101)
* [ReactのHigher Order Components詳解](http://postd.cc/react-higher-order-components-in-depth/)