JavaScript初心者がReact+Reduxでブログのフロントエンドを作成するまで

  • 7
    Like
  • 0
    Comment

はじめに

JavaScriptの基礎知識がだいたいわかったので、スキマ時間(4時間くらい)で簡単なブログのフロントエンドをReact+Reduxで構築した。
といっても、Modern React with Reduxというコースのセクション9を見ながら手を動かしただけ。JavaScript初心者だが、セクション9から見始めても特に問題なかった。

ソースコード

実装時のメモも含めて、以下に保存してある。
github.com/tomoyamachi/react-blog-sample

できるようになったこと

  • SPAだが、URLごとで表示するDOMを分ける
  • 非同期でAPI通信を行う
  • 非同期処理が完了してから、表示処理を実行する
  • 送信フォームの管理(バリデーション含む)
  • ESの言語仕様がざっくりわかる
  • 一人である程度のプロジェクトをつくる

今後

実践でそのまま利用できそうなことが詰まっていた。作成後1時間くらいはできたコードをいじって、各コードの役割を理解した。
次は、テストやコード品質の担保をしたいので、Advanced React and Reduxを見ようと思っている。

もし、React+Reduxを利用した中規模以上プロジェクトのソースコードが公開されてれば、かなりレベル上げができそう。
軽く探しているが見当たらないので、もしあれば知りたい。

以下、パッケージなどのメモ

SPAだが、URLごとで表示内容を出し分けたい

react-router-dom を利用。Switchを使わない場合、パスの正規表現に引っかかったものすべてのコンポーネントが表示される。

なお、parameterを取得したいときは、Componentの this.props.match.params.{name}の形で取得できる。

index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import reducers from './reducers';
import promise from 'redux-promise';
import PostsIndex from './components/posts_index';
import PostsNew from './components/posts_new';
import PostsShow from './components/posts_show';
const createStoreWithMiddleware = applyMiddleware(promise)(createStore);

ReactDOM.render(
  <Provider store={createStoreWithMiddleware(reducers)}>
    <BrowserRouter>
      <div>
        <Switch>
          <Route path="/posts/new" component={PostsNew} />
          <Route path="/posts/:id" component={PostsShow} />
          <Route path="/" component={PostsIndex} />
        </Switch>
      </div>
    </BrowserRouter>
  </Provider>
  , document.querySelector('.container'));

API通信がしたい

axiosを利用。
レスポンスが返ってきてからの処理を入れる場合は、axios.{HTTPmethod}().then() でつなげる。エラー時の挙動は.catch()で管理

actions/index.js
export function createPost(values, callback) {
    const request = axios.post(`${ROOT_URL}/posts${API_KEY}`, values)
          .then(() => callback())
          .catch((e) => cosole.log(e));
    return {
        type: CREATE_POSTS,
        payload: request
    };
}

非同期処理を管理したい

redux-promiseを利用する。ミドルウェアに入れるだけで、利用できるようになる。非同期処理が完了してから、処理を行えるようになる。 ※細かなことは分かってない

const createStoreWithMiddleware = applyMiddleware(promise)(createStore);

ReactDOM.render(
  <Provider store={createStoreWithMiddleware(reducers)}>
...(略)

フォームの管理がしたい

redux-form を利用。
バリデーションはreduxFormの引数にvalidate を指定すればいい。
フォーム操作後にエラーを表示したい場合は、meta.touchedで判定。

import React, {Component} from 'react';
import {Field, reduxForm} from 'redux-form';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';

class PostsNew extends Component {
    renderField(field) {
        const {meta: { touched, error }} = field;
        const className = `form-group ${touched && error ? 'has-danger' : ''}`;

        return (
            <div className={className}>
              <label>{field.label}</label>
              <input
                className="form-control"
                type="text"
                {...field.input}
                />
              <div className="text-help">
                {field.meta.touched ? field.meta.error : ''}
              </div>
            </div>
        );
    }

    onSubmit(values) {
        this.props.createPost(values, () => {
            this.props.history.push('/');
        });
    }

    render() {
        const { handleSubmit } = this.props;
        return (
            <form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
              <Field
                label="Title"
                name="title"
                component={this.renderField}
                />
              <button type="submit" className="btn btn-primary">Submit</button>
            </form>
        );
    }
}

function validate(values) {
    const errors = {};
    if(!values.title) {
        errors.title = "enter a title!";
    }
    return errors;
}

export default reduxForm({
    validate,
    form: 'PostsNewForm'
})(
    connect(null,{ createPost })(PostsNew)
);