LoginSignup
1
1

More than 5 years have passed since last update.

Falcor+Reactフルスタック(views層とcomponents層)

Last updated at Posted at 2017-08-14

Meteor and Reactによるリアクティブシステム「ラーメン野郎を追いかけろ! @ Twitter」を作ってみた

Falcor+Reactフルスタック(開発環境)
Falcor+Reactフルスタック(react-router)
Falcor+Reactフルスタック(views層とcomponents層)
Falcor+Reactフルスタック(formsy-react)
Falcor+Reactフルスタック(エラー処理)
Falcor+Reactフルスタック(Material-UI)

 今回は、Reactのプログラム構成についてです。Reactはviewを作成するライブラリですが、一般的には複数のcomponentsから成り立ちます。ではどのようにしてcomponentsを作成して組み合わせていけばよいのでしょうか?

 大雑把に言えば、一般的には1つの画面を作成するのに2つのcomponentsを作成します。2つのcomponentはviews層とcomponents層に属し親子関係にあります。viewsのcomponentが親で、components層のcomponentが子になります。

 views層のcomponentは外部とのデータのやり取りやハンドラの定義を行い、データやハンドラをcomponents層のcomponentに渡します。components層のcomponentはより低レベルで具体的な画面を描画します。

 具体的にユーザ登録画面で説明しましょう。ユーザ登録画面はviews層としてsrc/views/RegisterView.jsを使い、components層としてsrc/components/RegisterForm.jsを使います。この2ファイルで定義された2つcomponentでユーザ登録画面が作られています。
http://www.mypress.jp:3019/

src/views/RegisterView.js
import React from 'react';
import falcorModel from '../falcorModel.js';
import { RegisterForm } from '../components/RegisterForm.js';


export default class RegisterView extends React.Component {
  constructor(props) {
    super(props);
    this.register = this.register.bind(this);
    this.state = {
      error: null
    };
  }

  async register (newUserModel) {
    await falcorModel
      .call(['register'],[newUserModel])
      .then((result) => result);

    const newUserId = await falcorModel.getValue(['register', 'newUserId']);

    if (newUserId === 'INVALID') {
      const errorRes = await falcorModel.getValue('register.error');

      this.setState({error: errorRes});
      return;
    }

    this.props.history.push({pathname: '/login'}); 
  }

  render () {
    return (
      <div>
        <h1>登録画面</h1>
        <div style={{maxWidth: 450, margin: '0 auto'}}>
          <RegisterForm
            onSubmit={this.register} />
        </div>
      </div>
    );
  }
}

 このviews層ではregister()というハンドラを作成し、components層の子component(RegisterForm)にonSubmitプロパティで渡しています。このハンドラは非同期処理(Falcor通信)を含んでいるので、async/awaitが使われています。これが起動されるのはcomponents層ですが、動作はviews層で行われます(あくまで概念的にですが)。views層で外部との通信(Falcor通信)が行われています。ユーザ登録が成功すれば、react-routerの機能を使ってlogin画面にリダイレクトします。これは前回の説明通りです。

this.props.history.push({pathname: '/login'});
src/components/RegisterForm.js
import React from 'react';
import Formsy from 'formsy-react';
import { RaisedButton, Paper } from 'material-ui';
import DefaultInput from './DefaultInput';

export class RegisterForm extends React.Component {
  constructor(props) {
    super(props);
    this.enableButton = this.enableButton.bind(this);
    this.disableButton = this.disableButton.bind(this);
    this.state = {
      canSubmit: false
    }; 
  }

  enableButton() {
    this.setState({
      canSubmit: true
    });
  }

  disableButton() {
    this.setState({
        canSubmit: false
    });
  }

  render() {
    return (
      <Formsy.Form onSubmit={this.props.onSubmit} onValid={this.enableButton} onInvalid={this.disableButton}>
        <Paper zDepth={1} style={{padding: 32}}>
          <h3>ユーザ登録フォーム</h3>
          <DefaultInput name='username' title='ユーザ名' value="" required />
          <DefaultInput name='firstName' title='名前' value="" required />
          <DefaultInput name='lastName' title='苗字' value="" required />
          <DefaultInput name='email' validations='isEmail' validationError='This is not a valid email' title='メール' value="" required />
          <DefaultInput type='password' name='password' title='パスワード' value="" required />
          <div style={{marginTop: 24}}>
            <RaisedButton
            disabled={!this.state.canSubmit}
            secondary={true}
            type="submit"
            style={{margin: '0 auto', display: 'block', width: 150}}
            label={'登録'} />
          </div>
        </Paper>
    </Formsy.Form>
    );
  }
}

 このcomponents層のcomponentは、JSXでユーザ登録画面を定義しているだけのシンプルなものです。通常components層のものはこのように画面の定義だけで、処理は親から受け取ったハンドラを起動するだけです。ここでの注意点としては、フォームの定義にFormsyライブラリを使っていることと、Input要素としてより低レベルのcomponents層のcomponentであるDefaultInputを使っていることです。このフォーム周りは次回以降に説明するつもりです。

 前回の説明でRouteの定義を示しましたが、それぞれのパスが1画面に相当します。Routeで指定されたcomponentがviews層のcomponentであると言えます。PageNotFoundは例外ですが。

Routeの定義
<Route exact path='/' component={HomeApp} name='home' />
<Route path='/login' component={LoginView} name='login' />
<Route path='/logout' component={LogoutView} name='logout' />
<Route path='/dashboard' component={DashboardView} name='dashboard' />
<Route path='/register' component={RegisterView} name='register' />
<Route component={PageNotFound}/>

 views層では、falcor通信の他に、Redux Storeとのやり取りやminimongoとのやり取り(Meteorの場合)などが行われます。components層では親からデータを受け取り単純に画面を描画します。

1
1
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
1
1