最近っぽいnext.jsの設定

# この記事について
next.jsを友人と趣味の開発で使用することになったので
しばらく使っていなかった分調べ直して環境構築したメモ

## 使っているアーキテクチャ
- react
- next
- redux
- typescript
- styled-jsx

## ディレクトリ
```
pages(nextのお作法)
src(componentとか入れる用)
types(typescriptの定義ファイルいれる)
.babelrc
next.config.js
package.json
tsconfig.json
```

## 設定
```package.json
{
  "name": "next-sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@types/next": "^6.1.2",
    "@types/next-redux-wrapper": "^2.0.0",
    "@types/react": "^16.4.11",
    "@types/react-redux": "^6.0.6",
    "@zeit/next-typescript": "^1.1.0",
    "module-alias": "^2.1.0",
    "next": "^6.1.1",
    "next-redux-wrapper": "^2.0.0",
    "react": "^16.4.2",
    "react-dom": "^16.4.2",
    "react-redux": "^5.0.7",
    "redux": "^4.0.0"
  },
  "devDependencies": {
    "@types/node": "^10.9.1",
    "babel-plugin-module-resolver": "^3.1.1",
    "fork-ts-checker-webpack-plugin": "^0.4.9",
    "redux-devtools-extension": "^2.13.5",
    "typescript": "^3.0.1"
  },
  "_moduleAliases": {
    "~": "src"
  }
}

```
ポイント
- `_moduleAliases` をつけないと`~`をaliasに設定した時typecheckで弾かれる



```tsconfig.json
{
  "compileOnSave": false,
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "jsx": "preserve",
    "allowJs": true,
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "removeComments": false,
    "preserveConstEnums": true,
    "sourceMap": true,
    "skipLibCheck": true,
    "baseUrl": "./",
    "paths": {
      "~/*": ["src/*"]
    },
    "noImplicitThis": true,
    "lib": [
      "dom",
      "es2016"
    ],
    "typeRoots":[
      "node_modules/@types",
      "./types"
    ],
    "rootDirs": ["./pages", "./src"]
  }
}
```

```
{
  "presets": [
    "next/babel",
    "@zeit/next-typescript/babel"
  ],
  "plugins": [
    [
      "module-resolver", {
        "alias": {
          "~": "./src"
        }
      }
    ]
  ]
}
```

```next.config.js
const withTypescript = require('@zeit/next-typescript')
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
const path = require('path')

module.exports = withTypescript({
  webpack(config, options) {
    config.resolve.alias = {
      ...config.resolve.alias,
      '~': './src'
    }

    // typecheckする
    if (options.isServer) {
      config.plugins.push(new ForkTsCheckerWebpackPlugin())
    }
    
    return config
  }
})
```

## tsxファイル
```_app.tsx
import React from 'react'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import App, { Container } from 'next/app'
import withRedux from 'next-redux-wrapper'
import { devToolsEnhancer } from 'redux-devtools-extension'

const reducer = (state = {foo: ''}, action) => {
  switch (action.type) {
    case 'FOO':
      return {...state, foo: action.payload};
    default:
      return state
  }
};

const makeStore = (initialState, _) => {
  return createStore(
    reducer,
    initialState,
    devToolsEnhancer({})
  )
}

interface AppProps {
  Component: React.Component
  pageProps: any
  store: any
}

class MyApp extends App<AppProps> {
  static async getInitialProps({ Component, ctx }) {
    ctx.store.dispatch({type: 'FOO', payload: 'foo'})

    const pageProps =
      Component.getInitialProps ? await Component.getInitialProps(ctx) : {}

    return { pageProps }
  }

  render() {
    const { Component, pageProps, store } = this.props

    return (
      <Container>
        <Provider store={ store }>
          <Component { ...pageProps } />
        </Provider>
      </Container>
    )
  }
}

export default withRedux(makeStore)(MyApp)
```
- storeは`src/store`にディレクトリを作ってやっていく予定
- reduxは使い込んでいないのでいまいちわかっていない。オススメの記事あったら教えてください。

```pages/index.tsx
import React, { Component } from 'react'
import { connect } from 'react-redux'

// Components
import DefaultLayout from '~/components/layouts/default'

interface PageProps {
  foo: string
  custom: string
}

class Page extends Component<PageProps> {
  static getInitialProps({ store }) {
    store.dispatch({type: 'FOO', payload: 'foo'})
    
    return { custom: 'custom', ...store.getState() }
  }

  render() { 
    return (
      <DefaultLayout>
        <p>Props from Redux { this.props.foo }</p>
        <p>Props from getInitialProps { this.props.custom }</p>
        <p>welcome to next.js!</p>
        <style jsx>
        {`
          p {
            color: red;
          }
        `}
        </style>
      </DefaultLayout>
    )
  }
}

export default connect()(Page)
```
```src/components/layouts/default/index.tsx
interface DefaultLayoutProps {
  children: React.ReactNode
}

const DefaultLayout: React.SFC<DefaultLayoutProps> = (props: DefaultLayoutProps) => {
  return (
    <div>
      <header>header</header>
      { props.children }
      <footer>footer</footer>
    </div>
  )
}

export default DefaultLayout
```
- components内はできるだけSFCで書けるように
- `export const`だとreact dev toolでcomponent名がunknownになっちゃう・・・

```types/styled-jsx.d.ts
import 'react'

declare module 'react' {
  interface StyleHTMLAttributes<T> extends React.HTMLAttributes<T> {
    jsx?: boolean
    global?: boolean
  }
}
```
- styled-jsx使うための定義ファイル

## 最後に
- 環境構築自体は使うものを決めてしまえばやはり使いやすい
- 暫定でやっているところが多いので（特にstore周り）作り込んでいったらテンプレートとしてgithubにおいておきたい
- typescriptは最近使い始めてあんまりわかっていないと思うので、今の時点で何かあれば教えてください

next.jsは1年近く使っていましたが、最近使っていなかったです。
気づいたら_app.jsとかもできてた・・・containerって名前、前のプロジェクトとかでも使っていたけど
もうnextで使われているから気をつけないとと思いました。