reduxについての自分的なメモ

reduxについての自分的なメモ

先日reduxについていろいろ学んだので、自分なりにまとめてみました.多少間違っている箇所あるかもしれませんが,温かい目で指摘してもらえると助かります.

reduxは以下の記事が参考にしつつ頑張りました.

hello world

今回はcreate-react-appを利用して書いていきました.

npm install -g create-react-app
create-react-app my-app

以上のコマンドでmy-appというディレクトリができると思います. cdでそのディレクトリに移動して中身を確認します.

~/my-app >>  ls
README.md         node_modules      package-lock.json package.json      public            src
~/my-app >>  tree src/
src/
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
└── registerServiceWorker.js

以下のコマンドを実行することでlocalhost:3000にアクセスするとWelcome to Reactと表示されると思います.

npm install
npm start

reduxアーキテクチャの導入

今のままだとただのReactなので,少しずつReact + Reduxの形にしていきます. 今回は以下のサンプルを参考にコードを書いていきます.

とりあえずいらないファイルを削除します.

rm src/App.js
rm src/App.css
rm src/App.test.js
rm src/index.css
rm src/logo.svg
rm src/registerServiceWorker.js

reduxをinstallします.

npm install redux
npm install react-redux  

src/index.jsの内容を変更します.

  • src/index.js
import React from 'react';
- import ReactDOM from 'react-dom';
- import './index.css';
- import App from './App';
- import registerServiceWorker from './registerServiceWorker';
+ import { createStore } from 'redux';
+ import { Provider } from 'react-redux';
+ import App from './components/App';
+ import reducer from './reducers';
+ import { render } from 'react-dom';

- ReactDOM.render(<App />, document.getElementById('root'));
- registerServiceWorker();

+ const store = createStore(reducer)
+ render(
+   <Provider store={store}>
+     <App />
+   </Provider>,
+   document.getElementById('root')
+ )

必要なディレクトリを作成していきます.

mkdir reducers
mkdir containers
mkdir components
mkdir actions

とりあえずcomponentにApp.jsを作成します. このApp.jsがindex.jsに呼ばれて,こいつがいろいろ呼んでいきます.

  • components/App.js
import React, { Component } from 'react';
import AddUser from '../containers/AddUser'

class App extends Component {
    render() {
        return (
            <div className="App">
                <AddUser />
            </div>
        );
    }
}

export default App;

container

とりあえずAddUserというcontainerを呼びます. このcontainerとはcomponentを呼んだりactionを呼んだりする発火点的な役割の場所です.

  • containers/AddUser.js
import React from 'react'
import { connect } from 'react-redux'
import { addUser } from '../actions'

let AddTodo = ({ dispatch }) => {
  let input

  return (
    <div>
      <form onSubmit={e => {
        e.preventDefault()
        if (!input.value.trim()) {
          return
        }
        dispatch(addUser(input.value))
        input.value = ''
      }}>
        <input ref={node => {
          input = node
        }} />
        <button type="submit">
          Add User
        </button>
      </form>
    </div>
  )
}
AddTodo = connect()(AddTodo)

export default AddTodo

これはpreventDefault()を呼ぶことによって本来のsubmitを止めて,dispatchを呼んだ後inputを空にする処理を記述しています. 試しにe.preventDefault()を消すとページが更新されてしまいます.ページが更新されてしまうとフロント側で保持していた状態が消えてしまうのでとりあえずページ更新を行わないようにする感じです.

ここでAddUser.jsでは,dispatchが呼ばれています.この関数はactionの関数を引数として呼び出します.そしてactionreducerを呼んで,その後reducerが状態を更新するという処理が実行されます.詳しくは後述します.

action

actionを作成します.このactionは,dispatch関数で呼ばれる関数を記述する場所です.

  • actions/index.js
let nextTodoId = 0
export const addUser = (text) => ({
    type: 'ADD_USER',
    id: nextTodoId++,
    text
})

ここではaddUserという関数を定義しており,これはtextをもらってきて,type, id, status, textを入れたオブジェクトを返します.

reducer

reducerはactionの返したオブジェクトを受け取り,action.typeによって処理をいろいろと変えられる場所です.

  • reducers/index.js
import { combineReducers } from 'redux'
import users from './users'

const App = combineReducers({
  users
})

export default App
  • reducers/users.js
const users = (state = [], action) => {
  switch (action.type) {
    case 'ADD_USER':
      return [
        ...state,
          {
              id: action.id,
              name: action.text
          }
      ]
    default:
      return state
  }
}

export default users

index.jscombineReducersを呼んでいることがわかると思います.これは複数のreducerをまとめることができる凄いやつです. 肝心のreducerは別ファイルのusers.jsに書いておき,index.jsで呼び出します. これで複数ファイルにreducerを分けられます.(便利)

reducerでやっていることは,action.typeでswitch caseを書いて,変更した状態をreturnする感じです.今回のstateはArrayですが,Objectなども返せます.

これで一通りの登録処理が終わったので,ここから追加したuserを表示するcomponentを作成していきます.

UserListの作成

追加されたUserを表示するためのcontainerとcomponentを作成していきます. まずcontainerから.

  • UserList.js
import { connect } from 'react-redux'
import UserTable from '../components/UserTable'

const mapStateToProps = (state) => ({
  users: state.users
})

const UserList = connect(
  mapStateToProps
)(UserTable)

export default UserList

connectというものが呼ばれているのがわかります.これはstateやらactionやらを渡してcomponentを作成できる便利なやつです. 渡されたstateやcomponentはcomponentがそのまま使用できます.

次にcomponentです.

  • UserTables.js
import React from 'react';

const UserTables = (users) => (
    <table>
        {users.users.map(user =>
            <tr><td>{user.id}</td><td>{user.name}</td></tr>
        )}
    </table>
);

export default UserTables;

これでnpm startすると以下のようなページになっていると成功です.

f:id:MitubaEX:20171215000115p:plain

ユーザを追加するとこんな感じです.

f:id:MitubaEX:20171215000147p:plain

所感

reduxはexample見ても頭がぶっ飛んでいたので,一から自分で作りました. 他にも使っていない機能(middlewareなど)があるので,それもまた触っていきたい.