es6
React
ESLint
react-router

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

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

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

render propsを使う。

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

  • <Route component>
  • <Route render>
  • <Route children>

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

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

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を追加して渡してる。

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を参照しよう。

さあ、recomposeを設置しよう。

  yarn add recompose

既存のサンプルからpure component(=stateless component)をarrow functionで直してみた。
pure componentの場合は、class式よりfunctionで作るのがもっといい方法らしい。
Building HOCs with Recomposeの記事になぜ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のほうがもっと容易である。
コードを組むときはテストしやすい形で組むのが正しい。

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,
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を作った。

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"が表示される。

参考