LoginSignup
50
20

More than 5 years have passed since last update.

Redux + React + ES6 の簡単アプリを作成する②(ReactとReduxの連携編)

Last updated at Posted at 2016-06-18

概要

  • 前回記事の続き。
  • 実際にReduxでReactのstateを管理する。
  • Reactのインストールは出来ている前提とする。
  • ReactとReactDOMはwebpackでグローバル変数かしている事とする。

Components

ReactでComponentsを作成していく。
まずは、メインとなるComponent。
(CSSフレームワークはMaterialize.cssを利用している。)

src/components/AppComponents.jsx
import FormApp from './Form/FormApp.jsx';

export default class App extends React.Component {
  render() {
    return (
      <div className="container">
        <FormApp
          handleClick={this.props.onClick}
          value={this.props.value}
        />
      </div>
    );
  }
}

App.propTypes = {
  onClick: React.PropTypes.func,
  value: React.PropTypes.string
};

次に、Form全体を括るComponent。

src/components/Form/FormApp.jsx
import FormInput from './FormInput';
import FormDisplay from './FormDisplay';

export default class FormApp extends React.Component {
  render() {
    return (
      <div>
        <div className="row">
          <div className="row">
            <h1>Redux + React</h1>
          </div>
          <div className="row">
            <div className="col s12 m4 l4">
              <FormInput
                className="input-text"
                handleClick={this.props.handleClick}
              />
            </div>
          </div>
          <div className="row">
            <FormDisplay
              value={this.props.value}
            />
          </div>
        </div>
      </div>
    );
  }
}

FormApp.propTypes = {
  handleClick: React.PropTypes.func,
  value: React.PropTypes.string,
};

次は、Input部分のComponent。

src/components/Form/FormInput.jsx

export default class FormInput extends React.Component {
  constructor(props) {
    super(props);
    this.send = this.send.bind(this);
  }
  send(e) {
    e.preventDefault();
    this.props.handleClick(this.refs.inputText.value);
    this.refs.inputText.value = '';
    return;
  }
  render() {
    return (
      <form>
        <input
          type="text"
          defaultValue=""
          ref="inputText"
        />
        <button
          className="btn"
          onClick={this.send}
        >
          Send
        </button>
      </form>
    );
  }
}

FormInput.propTypes = {
  handleClick: React.PropTypes.func
};

最後に表示部分のComponent。

src/components/Form/FormDisplay.jsx

export default class FormDisplay extends React.Component {
  render() {
    return (
      <div>{this.props.value}</div>
    );
  }
}

FormDisplay.propTypes = {
  value: React.PropTypes.string
};

Container

ReduxでReactを扱う時に、最初にはまるのはContainerの作成だと思います。
Reduxを使う上では、ReactのComponentは2種類に分けられます。

Container Components

機能に関するコンポーネント。いわゆるFluxでいうContainerです。
主に直接Reduxと連携するコンポーネントで、ReduxのStoreの状態(state)を監視し、またReduxのActionをDispatchする役割を持ち、データを所得したり、stateの更新を行ったりします。
主に親コンポーネントがこの役割を担います。

Presentational Components

見た目に関するコンポーネント。
Container componentsからpropsを通してデータを受け取り、Viewを構築します。
また同様にpropsから受け取ったコールバックを実行します。
前述した、Componentはこちらにあたる。

Container Componentsを実装していきます。

src/containers/AppContainer.jsx
import { connect } from 'react-redux';
import App from './../components/AppComponents';
import FormActions from './../actions/FormActions';


let mapStateToProps = (state) => {
  return {
    value: state.value
  };
};

let mapDispatchToProps = (dispatch) => {
  return {
    onClick(value) {
      console.log(value);
      dispatch(FormActions.send(value));
    }
  };
};

const AppContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(App);

export default AppContainer;

状態管理の部分はContainerComponentsで行います。
最後のconnectの部分で、PresentationalComponentsを引数にとり、ReuxとReactを連携します。

エントリーポイント

最後にエントリーポイントを実装します。

src/index.jsx
/*::::::::::::::::::::::::::::::::::
 Components
:::::::::::::::::::::::::::::::::::*/
import App from './containers/App';
import { createStore } from 'redux';
import Reducer from './reducers/App';
import { Provider } from 'react-redux';

/*::::::::::::::::::::::::::::::::::
 InitialState
:::::::::::::::::::::::::::::::::::*/
const initialState = {
  value: ''
};
/*::::::::::::::::::::::::::::::::::
 InitialStore
:::::::::::::::::::::::::::::::::::*/
const store = createStore(Reducer, initialState);

/*::::::::::::::::::::::::::::::::::
 InitialDOM
:::::::::::::::::::::::::::::::::::*/
let mountNode = document.getElementById('mountNode');

/*::::::::::::::::::::::::::::::::::
 Rendering
:::::::::::::::::::::::::::::::::::*/
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>
  , mountNode);

InitialStateでstateを初期化し、
InitialStoreで引数にReducerとstateの初期値を渡します。
これによりStoreが作成されます。

最後にContainerComponentsをrenderするのですが、この際にreact-reduxが提供する[Provider]コンポーネントを親としてrenderします。
Providerコンポーネントはパラメータとして、storeをとり、ここに作成したstoreを与える事で、PresentationalComponentsのpropsの値としてstateが使えるという仕組みです。

終わり

Redux自体は簡単なものの、ReactとReduxの連携の部分が少しデリケートで戸惑いましたが、
簡単アプリの作成に成功しました。
データがAction->(Dispatch)->Reducer->Storeと一方向になる事で、大・中規模のシステムでも対応しやすくなるのではないかと思います。
次はFluxをもう少し掘り下げての勉強と、Reduxでの非同期通信(Actionに記載?)周りを調査したいと思います。

50
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
50
20