Reactで表示内容(headerとかfooterとか）のパーツ化・共通化


いわゆるheader, main, footerとかで構成されるサイトにて、headerやfooterを共通パーツ化したい。
（逆に言えば、headerやfooterはそのままにmainの内容を切り替えたい）

![スクリーンショット 2019-12-12 10.53.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55188/a9d2dfac-a3dd-3915-53c2-94e2b1f6f197.png "スクリーンショット 2019-12-12 10.53.53.png")



## 前提

* ルーティングにはreact-router-domを利用する
* よく[core-ui(React)](https://github.com/coreui/coreui-free-react-admin-template)を利用するので、そこでのやり方を踏襲


## 実装：基本編

### App.js

App.jsですべてのルーティングを設定せず、レイアウトや認証が異なるといったレベルでルーティングを設定する。
基本的にDefaultコンポーネントに流す。

```jsx
import React from 'react';
import { BrowserRouter, Switch, Route } from 'react-router-dom';

//screens
import Default from './screens/Default';
import Login from './screens/Login';

class App extends React.Component {
    render() {
        return (
            <BrowserRouter>
                <Switch>
                    <Route exact path="/login" component={Login} />
                    <Route path="/" component={Default} />
                </Switch>
            </BrowserRouter>
        );
    }
}

export default App;
```

### Default.js


Default.jsにて基本レイアウトを適用し、変更される部分（ここではmain）をSwitchで囲み、ルーティングを設定する。


```jsx
import React from 'react';
import { Switch, Route } from 'react-router-dom';

//screens
import Home from './Home';
import About from './About';
import Service from './Service';

class Default extends React.Component {
    render() {
        return (
            <div>
                <header style={styles.header}>head</header>
                <main style={styles.main}>
                    <Switch>
                        <Route exact path="/" component={Home} />
                        <Route path="/about" component={About} />
                        <Route path="/service" component={Service} />
                        <Route render={() => <p>not found!.</p>} />
                    </Switch>
                </main>
                <footer style={styles.footer}>footer</footer>
            </div>
        );
    }
}

export default Default;

//css
const styles = {
    header: {
        height: 100,
        background: "#ddd",
    },
    main: {
        height: 200,
    },
    footer: {
        height: 100,
        background: "#ddd",
    }
}
```

あとはそれぞれのページのレイアウトを行うだけ。

### Home.js

Homeの内容。Homeと表示、リンクを設定してるだけ。

```jsx
import React from 'react';
import { Link } from 'react-router-dom';

class Home extends React.Component {
    render() {
        return (
            <div>
                <p>Home</p>
                <p><Link to="/">Home</Link></p>
                <p><Link to="/about">About</Link></p>
                <p><Link to="/service">Service</Link></p>
            </div>
        );
    }
}

export default Home;
```

### About.js

基本Homeと同じですが、一応。

```jsx
import React from 'react';
import { Link } from 'react-router-dom';

class About extends React.Component {
    render() {
        return (
            <div>
                <p>About</p>
                <p><Link to="/">Home</Link></p>
                <p><Link to="/about">About</Link></p>
                <p><Link to="/service">Service</Link></p>
            </div>
        );
    }
}

export default About;
```

### Service.js

基本Homeと同じですが、一応。

```jsx
import React from 'react';
import { Link } from 'react-router-dom';

class Service extends React.Component {
    render() {
        return (
            <div>
                <p>Service</p>
                <p><Link to="/">Home</Link></p>
                <p><Link to="/about">About</Link></p>
                <p><Link to="/service">Service</Link></p>
            </div>
        );
    }
}

export default Service;
```

## 実装：応用編

コンポーネントが大量になると記述も大変だし、何より読み込みに時間がかかる。
core-uiのソースを見てるとroutesを変数化し、なおかつlazy()リードしている。

### routes.js

まず、読み込みやルートの定義は別ファイル化して下記のように記述します（場所はとりあえずsrc直下に置いています）。

```js:routes.js
import React from 'react';

const Home = React.lazy(() => import('./screens/Home'));
const About = React.lazy(() => import('./screens/About'));
const Service = React.lazy(() => import('./screens/Service'));

const routes = [
    { path: '/', exact: true, name: 'Home', component: Home },
    { path: '/about', exact: true, name: 'About', component: About },
    { path: '/service', exact: true, name: 'Service', component: Service },
]

export default routes;
```

### Home.js

Home.js等でroutes.jsをimportし、ループにてRouteを設定しています。
なお、lazy()を使うと、読み込みの間表示するコンテンツの指定を\<Suspense>\</Suspence>で行う必要があるようです。

```jsx:Default.js
import React, { Suspense } from 'react';
import { Switch, Route } from 'react-router-dom';

import routes from '../routes';

class Default extends React.Component {

    loading = () => <div>Loading...</div>

    render() {
        return (
            <div>
                <header style={styles.header}>head</header>
                <main style={styles.main}>
                    <Suspense fallback={this.loading}>
                        <Switch>
                            {
                                routes.map((route, index) => (
                                    <Route
                                        key={index}
                                        path={route.path}
                                        exact={route.exact}
                                        name={route.name}
                                        render={props => (<route.component {...props} />)}
                                    />
                                ))
                            }
                            <Route render={() => <p>not found!.</p>} />
                        </Switch>
                    </Suspense>
                </main>
                <footer style={styles.footer}>footer</footer>
            </div>
        );
    }
}

export default Default;

const styles = {
    header: {
        height: 100,
        background: "#ddd",
    },
    main: {
        height: 200,
    },
    footer: {
        height: 100,
        background: "#ddd",
    }
}
```