React で inputFrom のイベントハンドラーを汎用的に作る
TL;DL 🗒
成果物 🎉
動機 🤔
- 名前、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
このライブラリは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, {}) のほうがよい?
以上、おしまい。