javascript - 编写通用的 React 输入钩子
问题描述
我正在尝试编写一个通用的 React Hook 来允许我更新对象。
我参考了:Input Hook -(来源:https ://rangle.io/blog/simplifying-controlled-inputs-with-hooks/ )并做了一些更改:
import { useState } from "react";
export const useForm = initialObject => {
const [values, setValues] = useState(initialObject);
return {
values: values || initialObject,
setValues,
reset: () => setValues({}),
bind: {
onChange: (event) => {
setValues({
...values,
[event.target.id]: event.target.value
})
}
}
};
};
这适用于单级对象:
{ name: '', type: '' }
但对于具有嵌套值的对象:
{ name: '', type: '', price: { dollar: 5, cents: 20 } }
我太确定我应该如何替换[event.target.id]
来读取嵌套级别的对象。
有人可以建议吗?
更新:
import { useState } from "react";
export const useForm = initialObject => {
const [values, setValues] = useState(initialObject);
return {
values: values || initialObject,
setValues,
reset: () => setValues({}),
bind: {
onChange: (event) => {
// ###need make this part generic###
// event.target.id will be "price.dollar"
values['price']['dollar'] = event.target.value;
setValues({
...values
})
}
}
};
};
解决方案
一般来说,你的钩子应该接受 aname
并value
更新你的本地状态。显然你的钩子总是收到一个event
并且你提取event.target.id
作为name
字段的和event.target.value
作为字段的值。我建议您更新您的钩子以接收 aname
和 avalue
作为参数,并让使用该钩子的组件定义什么是name
和value
根据您的钩子,您可以像这样更新嵌套对象。请看一下这个例子。
import React, { useState } from "react";
import ReactDOM from "react-dom";
const useForm = initialObject => {
const [values, setValues] = useState(initialObject);
return {
values: values || initialObject,
setValues,
reset: () => setValues({}),
bind: {
onChange: event => {
setValues({
...values,
[event.target.id]: event.target.value
});
}
}
};
};
const App = () => {
const { values, bind } = useForm({
name: "",
type: "",
price: { dollar: 5, cents: 20 }
});
return (
<div>
Hook state:
<pre>{JSON.stringify(values, null, 4)}</pre>
<div>
<div>
<label>
Name : <br />
<input id="name" onChange={bind.onChange} />
</label>
</div>
<div>
<label>
Type : <br />
<input id="type" onChange={bind.onChange} />
</label>
</div>
<div>
<label>
Price - Dollar : <br />
<input
id="dollar"
type="number"
onChange={e => {
bind.onChange({
target: {
id: "price",
value: { ...values.price, [e.target.id]: e.target.value }
}
});
}}
/>
</label>
</div>
<div>
<label>
Price - Cents : <br />
<input
id="cents"
type="number"
onChange={e => {
bind.onChange({
target: {
id: "price",
value: { ...values.price, [e.target.id]: e.target.value }
}
});
}}
/>
</label>
</div>
</div>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
更新(2019 年 11 月 25 日):
但是,您可以按以下方式更新您的钩子
const useForm = initialObject => {
const [values, setValues] = useState(initialObject);
return {
values: values || initialObject,
setValues,
reset: () => setValues({}),
bind: {
onChange: event => {
setValues({
...values,
[event.target.id]: event.target.value
});
},
onNestedChange: (event, name) => {
setValues({
...values,
[name]: {
...values[name],
[event.target.id]: event.target.value,
}
})
}
}
};
};
然后在您的输入中,您可以编写如下:
<div>
<label>
Price - Dollar : <br />
<input
id="dollar"
type="number"
onChange={e => bind.onNestedChange(e, 'price')}
/>
</label>
</div>
<div>
<label>
Price - Cents : <br />
<input
id="cents"
type="number"
onChange={e => bind.onNestedChange(e, 'price')}
/>
</label>
</div>
这样你就为嵌套对象创建了另一个绑定方法,也许你可以添加另一个被调用array
的东西。希望这能给你一些关于如何改进钩子的想法。顺便说一句,有很多方法可以做到这一点,这只是一个例子。可能有更好的方法来做到这一点。
更新 2(2019 年 11 月 25 日):
我已经更新了你的useForm
钩子,现在你可以将嵌套对象属性设置为你的状态。但是,我没有使用数组进行测试,它可能会导致问题。
const useForm = initialObject => {
const [values, setValues] = useState(initialObject);
// Copied and modified from https://stackoverflow.com/a/18937118/11125492
const nestedObjectSet = (obj, path, value) => {
let schema = obj; // a moving reference to internal objects within obj
const pList = path.split(".");
const len = pList.length;
for (let i = 0; i < len - 1; i++) {
let elem = pList[i];
if (!schema[elem]) schema[elem] = {};
schema = schema[elem];
}
schema[pList[len - 1]] = value;
};
// handleOnChange update state value
const handleOnChange = event => {
let newValues = Object.assign({}, values);
nestedObjectSet(newValues, event.target.name, event.target.value);
setValues(newValues);
};
return {
values: values || initialObject,
setValues,
reset: () => setValues({}),
bind: {
onChange: handleOnChange
}
};
};
你可以这样使用它。请注意,我已将key
对象的 更改为从event.target.id
到event.target.name
。key
应该设置在而name
不是id
const App = () => {
const { values, bind } = useForm({
name: "",
type: "",
price: { dollar: 5, cents: 20 }
});
return (
<div>
Hook state:
<pre>{JSON.stringify(values, null, 4)}</pre>
<div>
<div>
<label>
Name : <br />
<input name="name" {...bind} />
</label>
</div>
<div>
<label>
Type : <br />
<input name="type" {...bind} />
</label>
</div>
<div>
<label>
Price - Dollar : <br />
<input name="price.dollar" type="number" {...bind} />
</label>
</div>
<div>
<label>
Price - Cents : <br />
<input name="price.cents" type="number" {...bind} />
</label>
</div>
</div>
</div>
);
};
推荐阅读
- javascript - chrome 处理 undefined 的方式不同吗?
- python - 从具有可变部分 fo 文件名的文件夹中读取多个 .csv 文件
- c++ - 为什么 QScrollArea 的大小受到限制?
- mysql - MySQL 选择后跟子查询
- javascript - 如何将异步函数传递给另一个函数并执行它?
- vba - Excel 文本到列格式化日期问题
- sql-server - “试图将非 NULL 列的值设置为 NULL。”
- sql-server - 在连接时过滤掉 NULL 值
- sql - 具有列参数的 T_SQL 函数
- google-bigquery - BigQuery 数据仓库设计?