devlog

主に web 開発とかプログラミングについて書きます

React で inputFrom のイベントハンドラーを汎用的に作る

TL;DL 🗒

成果物 🎉

f:id:nkgr:20180525214734p:plain

動機 🤔

  • 名前、Email 、住所など複数のインプットフォームのイベントハンドラーを別々に作るのは億劫
  • 階層的に作ったObject も扱いたい

バージョン情報 💻

  "dependencies": {
    "object-path": "^0.11.4",
    "react": "^16.3.2",
    "react-dom": "^16.3.2",
    "react-router-dom": "^4.2.2",
    "react-scripts": "1.1.4"
  },

How To ✏️

ReactのState の値を更新することを想定した場合、Stateオブジェクトは代替階層構造になる。 でも EventHander の引数に name, value を指定したい場合、name が 階層構造を含めたKey で value はその値になることが予測される。

通常Objectの階層の深い値を更新する場合、通常のこんな書き方になる

const data = {
    entry: {
        path: './src',
        filename: 'index.js'
    },
    output: {
        path: './public',
        filename: 'bundle.js'
    },
    devServer: true,
}

data.entry.path = 'aaa';
data.devServer = false;
console.log(data);

標準出力

{
        "entry": {
                "path": "aaa",
                "filename": "index.js"
        },
        "output": {
                "path": "./public",
                "filename": "bundle.js"
        },
        "devServer": false
}

こんな感じで Entry も devServer もReact コンポーネントに name や value をフラットに渡して変更された値を更新したいが、state オブジェクトが階層構造だと name が受け渡しにくい

<FormInput
          label="Entry file"
          name="path"
          value={this.state.entry.path}
          onChange={this.handleFormChange}
        />
<FormInput
          label="devServer"
          name="devServer"
          value={this.state.devServer}
          onChange={this.handleFormChange}
        />

  handleFormChange = ({ name, value }) => {
    this.setState({name, value});
  };

そこでobject-path の出番

object-path

GitHub - mariocasciaro/object-path: A tiny JavaScript utility to access deep properties using a path (for Node and the Browser)

このライブラリはobject の階層を リテラルで表現してset/get できるライブラリ

さっきの例でいくとこんな感じ

var objPath = require("object-path");

objPath.set(data, 'entry.path', './javascript');
objPath.set(data, 'output.filename', 'dist.js');
{
        "entry": {
                "path": "./javascript"
        },
        "output": {
                "filename": "dist.js"
        },
        "devServer": false
}

これをReact Component のイベントハンドラーに入れるとこんな感じ

  handleFormChange = ({ name, value }) => {
    let copyState = Object.create(this.state, {});
    objPath.set(copyState, name, value);
    this.setState(copyState);
  };

よく見るサンプルで State の値を更新するときに Object.assignが利用されるが Object.assignはシャローコピーなのでImmutable な実装をする場合は Object を新規作成でコピーできる Object.create(object, {}) のほうがよい?

以上、おしまい。