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については割愛する。)
次の記事にあるソースコードをそのまま使って直した。
--- 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を追加して渡してる。
--- 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のほうがもっと容易である。
コードを組むときはテストしやすい形で組むのが正しい。
--- 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,
--- 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を作った。
--- 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"が表示される。