#概要
前回の記事ではReactのみを用いて状態遷移を行いましたが、今回はReactJSが扱うUIのstate(状態)を管理をするためのフレームワークである__『Redux』__を用いて、前回と同様の機能を実装したいと思います。
前回の記事
『state』を用いて、入力された文字数をカウントしてみよう!
#パッケージのインストール
まずは、必要なパッケージのインストールを行いましょう!下記コマンドでredux
とreact-redux
をインストールできます。
$ yarn add redux react-redux
#なぜReduxが必要なのか
早速コードを書いていきたいのですが、まずはなぜ__『Redux』__というものが開発されたのかを調べたいと思います。公式のページをGoogle翻訳で訳して簡単にまとめたものを以下に載せます。
JavaScriptで開発されるシングルページアプリケーションが複雑になるにつれて、管理しなければいけない状態(state)の数が増大していき、新機能開発やバグ修正などがとても大変になります。その複雑さは『変化(mutation)』と『非同期性(asynchronicity)』という2つの概念を混在させていることにより起こるものです。Reactのようなライブラリは、非同期と直接DOM操作の両方を削除することによって、ビューレイヤでこの問題を解決しようとします。しかし、状態(state)の管理はユーザーに任せられたままでした。__『Redux』__はその状態(state)管理の複雑さを解消するために開発されました。
#Action
では実際にコードを見ながら理解を進めていきましょう!
まずは__『Action』__からです!
『Action』:今から『stateをこのように変化させます』といった情報を持っているオブジェクトのことです。
そのオブジェクトの中で、『type』というKeyとそのKeyに対応する値を持つのが『Action』の特徴です。その『type』の値はユニークなものでないといけません。『type』以外は自由に定義して大丈夫です。
{
type: TEXTCHANGE,
textValue: textValue,
textLength: textLength,
}
では続いて、作成したActionを返す関数__『Action Creator』__を書いていきましょう!
export const TEXTCHANGE = 'TEXTCHANGE';
export const textChange = (textValue, textLength) => {
return {
type: TEXTCHANGE,
textValue: textValue,
textLength: textLength,
};
};
TEXTCHANGE
はこの後のReducerでも使うので、複数箇所で活用されるデータについては1箇所で定義してそれを再利用しましょう!
#Reducer
『Reducer』:Actionが発生した時に、そのActionに組み込まれているtypeに応じて、状態をどう変化させるのかというのを定義したものです。
実際にコードを見てみましょう!
import {combineReducers} from 'redux';
import text from './text';
export default combineReducers({text});
//複数のreducerを持った場合
//combineReducers({text, foo, baz});
src/reducers/index.js
ではアプリケーション内で実装される__『Reducer』を1つにまとめる役割があります。今回は実装する『Reducer』が1つしかないのでcombineReducers({text})
となっておりますが、まとめる『Reducer』__が複数個ある場合はcombineReducers({text, foo, baz});
のようにcombineReducers
にまとめれば大丈夫です!
import {TEXTCHANGE} from '../actions';
const initialState = {textLength: 0, textValue: 'initial value'};
export default (state = initialState, action) => {
switch (action.type) {
case TEXTCHANGE:
return {textValue: action.textValue, textLength: action.textLength};
default:
return state;
}
};
src/reducers/text.js
ではstateとactionから新しいstateを作成してreturnで返します。ここではstateを更新しているのではなく、新しいstateを作成しているというところがポイントです!
#Store
『Store』:アプリケーション内のstate(状態)を保存しているところです。
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore} from 'redux';
import {Provider} from 'react-redux';
import './index.css';
import reducer from './reducers';
import App from './components/App';
import * as serviceWorker from './serviceWorker';
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
ここでは、Reducerをもとに『store』を作成します。まずは『store』を作成するための関数であるcreateStore
をreduxパッケージからimportしましょう。そしてimportしたreducerをcreateStore
の引数に使うことで、『store』を定義します。
次に、作成した『store』を、全Componentに渡すための機能を持つProvider
という特殊なComponentをreact-reduxからimportしましょう!そして各Componentを<Provide store={store}></Provider>
でラップすることで『store』のデータを渡すことができるようになります。
#connect
connect関数を用いて、今まで作成してきたstateやActionとComponentとの関連付けを行なって、viewのイベントで状態を遷移させて遷移後の状態を画面に再描画しましょう。
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {textChange} from '../actions';
class App extends Component {
render() {
const props = this.props;
console.log(props);
return (
<React.Fragment>
<div>文字数: {props.textLength}</div>
<textarea
type="text"
onChange={e => props.textChange(e.target.value, e.target.value.length)}
/>
</React.Fragment>
);
}
}
const mapStateToProps = state => {
return {
textValue: state.text.textValue,
textLength: state.text.textLength,
};
};
const mapDispatchToProps = dispatch => {
return {
textChange: (textValue, textLength) =>
dispatch(textChange(textValue, textLength)),
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
stateとactionをComponentに結びつけるためにconnect関数
をimportし、Componentの名前であるApp
を引数にとり、mapStateToProps
とmapDispatchToProps
に結びつけます。
mapStateToProps
はstateからComponentに必要な情報を抜き出して、propsとしてマッピングする機能を持つ関数です。引数にはstateを取って、どういうオブジェクトをpropsとして対応させるのかといったことを返します。
あるactionが発生した時にreducerにtypeに応じた状態遷移を実行させるための関数がdispatch
になります。mapDispatchToProps
では、このdispatch
関数を引数においてこのComponentにおいて必要なdispatch
関数を宣言します。今回はtextが入力された際に、入力された文字と文字数を表示したいので、textChange
をkeyにtextChange関数
を引数に持つdispatch関数
を値に定義した、mapDispatchToProps
を作成します。
#最後に
以上で実装は終わりです。前回の記事と同様のものがブラウザにレンダリングされていると思います。
次回は今まで学習してきた、ReactとReduxを使ってTodoアプリケーションを作成する方法をまとめたいと思います!
#リファレンス