HighBridge’s blog

勉強の記録

2021/8/18

Node.js + React

メモ

  • 3-5から開始
  • 正規表現は勉強しといた方が良さそう。
  • プロパティの型の定義を学んだ
  • FormInputというコンポーネントで、valueとnameとfilter, onChangeという4つのプロパティを利用できるものとする例
// コンポーネントの定義 
class FormInput extends Component {...}

// PropTypes型を使うための宣言
import PropTypes from 'prop-types'

// プロパティの型の宣言
FormInput.propTypes = {
  value: PropsTypes.string, // 文字列型
  name: PropsTypes.string.isRequired, // 文字列型で指定が必須
  filter: PropsTypes.object, // オブジェクト型
  onChange: PropTypes.func // 関数型
} 

FormInput.defaultProps = {
  filter: null,
  pattern: null,
  value: '',
  onChange: null
}
import React, {Component} from 'react'
import PropTypes from 'prop-types'

// 汎用的な入力コンポーネント
export default class FormInput extends Component {
  constructor (props) {
    super(props)
    const v = this.props.value
    this.state = {
      value: v,
      isOK: this.checkValue(v)
    }
  }

  // パターンに合致するかチェック
  checkValue (s) {
    if (this.props.pattern === null) {
      return true
    }
    return this.props.pattern.test(s)
  }

  // 値がユーザーにより変更された時
  handleChange (e) {
    const v = e.target.value
    // フィルタが変更されていればフィルタを適用する
    const filter = this.props.filter
    let newValue = v
    if (filter !== null){
      newValue = newValue.replace(filter, '')
    }
    const newIsOK = this.checkValue(newValue)
    this.setState({
      value: newValue,
      isOk: newIsOK
    })
    // イベントを実行する
    if(this.props.onChange) {
      this.props.onChange({
        target: this,
        value: newValue,
        isOK: newIsOK,
        name: this.props.name
      })
    }
  }
  
  // プロパティが変更された時
  componentWillReceiveProps (nextProps) {
    this.setState({
      value: nextProps.value,
      isOK: this.checkValue(nextProps.value)
    })
  }

  //描画
  render () {
    const msg = this.renderStatusMessage()
    return (<div>
      <label>{this.props.label}: <br />
        <input type="text"
          name={this.props.name}
          placeholder={this.props.placeholder}
          value={this.state.value}
          onChange={e => this.handleChange(e)} />
        {msg}
      </label>
    </div>)
  }

  renderStatusMessage() {
    const so = {
      margin: '8px',
      padding: '8px',
      color: 'white'
    }
    let msg = null
    if (this.state.isOK){
      so.backgroundColor = 'green'
      msg = <span style={so}>OK</span>
    } else {
      if (this.state.value !== ''){
        so.backgroundColor = 'red'
        msg = <span style={so}>NG</span>
      }
    }
    return msg
  } 
}

// プロパティの型を定義
FormInput.propType = {
  name: PropTypes.string.isRequired,
  label: PropTypes.string.isRequired,
  filter: PropTypes.object,
  pattern: PropTypes.object,
  value: PropTypes.string,
  placeholder: PropTypes.string,
  onChange: PropTypes.func
}

// プロパティの初期値を定義
FormInput.defaultProps = {
  filter: null,
  pattern: null,
  value: '',
  placeholder: '',
  onChange: null
}
  • DOMにReactで直接アクセスするにはrefプロパティを使う。このプロパティには、コールバック関数を指定するのだが、ReactがDOMを直接インスタンス化する際に実行される。コールバック関数が実行されるとき、インスタンス化したDOMオブジェクトが引数として得られる。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>

  <script src="https://unpkg.com/react@15/dist/react.min.js"></script>
  <script src="https://unpkg.com/react-dom@15/dist/react-dom.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.38/browser.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
    // フォームコンポーネント
    class LoginForm extends React.Component {
      constructor (props){
        super(props)
        this.state = {
          user: '',
          pass: ''
        }
      }

      render() {
        const doSubmit = e => this.doSubmit(e)
        const doChange = e => this.doChange(e)
        // 描画内容
        return (<form onSubmit={doSubmit}>
          <label>ユーザー名: <br />
            <input type="text" name="user"
              ref={ (i)=>{ this.user = i} }
              value={this.state.user}
              onChange={doChange} />  
          </label><br />
          <label>パスワード: <br />
            <input type="text" name="pass"
              ref={ (i)=>{ this.pass = i} }
              value={this.state.pass}
              onChange={doChange} />  
          </label><br />
          <input type="submit" value="送信" />
        </form>)
      }

      doChange (e) {
        const key = e.target.name
        this.setState({
          [key]: e.target.value
        })
      }

      // refプロパティで要素を取得しているのでthis.userでDOMにアクセスできる
      doSubmit (e) {
        e.preventDefault()
        // 値が空の場合にinput要素をフォーカス
        if (!this.state.user) {
          this.user.focus()
          return
        }
        if (!this.state.pass) {
          this.pass.focus()
          return
        }
        window.alert(JSON.stringify(this.state))
      }
    }
    // DOMを書き換え
    ReactDOM.render(
      <div><LoginForm /></div>,
      document.getElementById('root')
    )
  </script>
  
</body>
</html>
  • コンポーネントのrender()メソッドで返すオブジェクトは実際に、DOMに描画されるオブジェクトとは全く関係がない。Reactはrender()メソッドの戻り値を元にして、実際のDOMを生成するので、戻り値はDOMオブジェクトではないことに注意。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>

  <script src="https://unpkg.com/react@15/dist/react.min.js"></script>
  <script src="https://unpkg.com/react-dom@15/dist/react-dom.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.38/browser.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
    class MyCompo extends React.Component {
      constructor(props){
        super(props)
        this.state = {value: ''}
      }

      render() {
        this.preInput = <input
          type="text"
          ref={i => { this.realInput = i }}
          onClick={e => this.doClick(e)} />
        
        return (
          <div>{this.preInput}</div>
        )
      }

      doClick (e) {
        // 合致するか
        console.log(this.preInput)
        console.log(this.realInput)
        if (this.preInput === this.realInput) {
          console.log('同じ')
        }else{
          console.log('異なる')
        }
      }
    }

    // DOMを書き換え
    ReactDOM.render(
      <div><MyCompo /></div>,
      document.getElementById('root')
    )

  </script>
  
</body>
</html>

SuperAgent(Ajax通信をするライブラリ)の使い方

  1. SuperAgentのライブラリを取り込む
  2. request.get(url).end(callback)を呼ぶ
  3. データを取得した時のコールバック関数を記述
// 機能を取り込み
const request = require('superagent')

// 指定のURLからデータを取得する
const URL = 'http://localhost:3000/fruits.json'
request.get(URL)
        .end(callbackGet)

// データを取得した時の処理
function callbackGet (err, res) {
  if(err) {
    // 取得できなかった時の処理
    return
  }
  // ここで取得した時の処理
  console.log(res.body)
}

機能を確認

  • GETメソッドを送信する際にURLパラメータを指定したい場合にはquery()メソッドを呼び出す
const params = {q: 'search',  uid: 100}
request.get(URL)
         .query(params)
         .end(callback)
  • ヘッダーに情報を与える場合にはset()メソッド
request.get(URL)
       .set('API-KEY', 'xxxxxxxx')
       .end(callback)
  • POSTメソッドを送信する場合にはget()ではなくpost()メソッドを呼び出す。この場合はsend()メソッドでパラメータを指定する。
request.protocols(URL)
       .set('Content-Type', 'application/json')
       .send( {name: 'hoge', age: 21})
       .end(callback)
  • URLパラメータを指定する場合にはquery()で行い、リクエスト本体にパラメータを指定する場合にはsend()メソッドを使う。
request.post(URL)
       .set('Content-Type', 'application/json')
       .query( {mode: 'aave', userid: 100})
       .send( {name: 'hoge', age: 21})
       .end(callback)