英文:
useCallback not updating array with object
问题
我正在尝试使用useCallback
更新包含对象的数组,但它似乎不是在更新对象,而是在添加或追加一个对象。
const items = [
{
id: 101,
name: 'Mae Jemison',
},
{
id: 201,
name: 'Ellen Ochoa',
},
];
const [headings, setHeadings] = useState(items);
const handleHeadingTextChange = useCallback(
(value, id) => {
let items2 = headings;
items2 = items2.map((item, key) => {
if (items2[key].id === id) {
items2[key].name = value;
}
return items2;
});
setHeadings((prevState) => [...prevState, items2]) // 添加,而不是更新
//setHeadings((prevState) => [...prevState, ...items2]) // 尝试2:仍然在添加
},
[setHeadings],
);
<input type="text" id="101" value="" onChange={handleHeadingTextChange} />
所以,预期的输出是在更改后:
items = [
{
id: 101,
name: 'Johaan',
},
{
id: 201,
name: 'Ellen Ochoa',
},
];
但实际上我得到的是:
items = [
{
id: 101,
name: 'Johaan',
},
{
id: 201,
name: 'Ellen Ochoa',
},
[
{
id: 101,
name: 'Johaan',
},
{
id: 201,
name: 'Ellen Ochoa',
},
],
];
我不确定如何在setHeading
函数中设置值,以便它只更新值而不是追加一个。是否有办法使其仅进行更新?
英文:
I am trying to update array with objects using useCallback but instead of updating object it is adding or appending one too.
const items = [
{
id: 101,
name: 'Mae Jemison',
},
{
id: 201,
name: 'Ellen Ochoa',
},
];
const [headings, setHeadings] = useState(items);
const handleHeadingTextChange = useCallback(
(value, id) => {
let items2 = headings;
items2 = items2.map((item, key) => {
if (items2[key].id == id) {
items2[key].name = value
}
return items2
});
setHeadings((prevState) => [...prevState, items2]) // adding, not updating
//setHeadings((prevState) => [...prevState, ...items2]) // try 2 : still adding
},
[setHeadings],
);
<input type="text" id="101" value="" onChange={handleHeadingTextChange} />
So, on change expected output is
items = [
{
id: 101,
name: 'Johaan',
},
{
id: 201,
name: 'Ellen Ochoa',
},
];
But Instead I am getting
items = [
{
id: 101,
name: 'Johaan',
},
{
id: 201,
name: 'Ellen Ochoa',
},
[{
id: 101,
name: 'Johaan',
},
{
id: 201,
name: 'Ellen Ochoa',
}]
];
I am not sure how to set value in setHeading function so that it only update the value and not appending one too. Is there way to make it update only?
答案1
得分: 1
问题
.map
遍历整个数组。如果你只想更新 单个 项,这就没有意义。onChange
接受一个事件处理程序,但你的处理程序接受(value, id)
<input>
的value
属性应该从对象的name
属性设置
解决方案
function MyComponent() {
const [headings, setHeadings] = useState(items);
const updateName = key => event => {
setHeadings(prevState => [
// 要更新的键前的元素
...prevState.slice(0, key),
// 要更新的元素
{ ...prevState[key], name: event.currentTarget.value },
// 键后的元素
...prevState.slice(key + 1),
])
}
return headings.map((h, key) =>
<input key={key} id={h.id} value={h.name} onChange={updateName(key)} />
)
}
改进
想象一下每次都要编写这个函数,当你有一个带有数组或对象状态的组件时。将其提取为通用函数,根据需要在其他地方使用 -
// 通用函数
function arrUpdate(arr, key, func) {
return [...arr.slice(0, key), func(arr[key]), ...arr.slice(key + 1)]
}
function objUpdate(obj, key, func) {
return { ...obj, [key]: func(obj[key]) }
}
// 简化的组件
function MyComponent() {
const [headings, setHeadings] = useState(items);
const updateName = key => event => {
setHeadings(prevState =>
// 在键处更新数组
arrUpdate(prevState, key, elem =>
// 更新元素的name属性
objUpdate(elem, "name", prevName =>
// 新元素的name
event.currentTarget.value
)
)
)
}
return headings.map((h, key) =>
<input key={key} id={h.id} value={h.name} onChange={updateName(key)} />
)
}
多个箭头函数?
柯里化函数使得编写事件处理程序更容易,但也许你以前从未使用过它们。这是未柯里化版本的样子 -
function MyComponent() {
const [headings, setHeadings] = useState(items);
const updateName = (key, event) => {
setHeadings(prevState =>
// 在键处更新数组
arrUpdate(prevState, key, elem =>
// 更新元素的name属性
objUpdate(elem, "name", prevName =>
// 新元素的name
event.currentTarget.value
)
)
)
}
return headings.map((h, key) =>
<input key={key} id={h.id} value={h.name} onChange={e => updateName(key, e)} />
)
}
英文:
problems
.map
iterates over the entire array. that doesn't make sense if you are trying to just update a single item.onChange
takes an event handler, but your handler accepts(value, id)
- the
value
property of your<input>
should be set from the object'sname
property
solution
function MyComponent() {
const [headings, setHeadings] = useState(items);
const updateName = key => event => {
setHeadings(prevState => {
return [
// elements before the key to update
...prevState.slice(0, key),
// the element to update
{ ...prevState[key], name: event.currentTarget.value },
// elements after the key
...prevState.slice(key + 1),
]
})
}
return headings.map((h, key) =>
<input key={key} id={h.id} value={h.name} onChange={updateName(key)} />
)
}
improvement
Imagine having to write that function each time you have a component with array or object state. Extract it to a generic function and use it where necessary -
// generic functions
function arrUpdate(arr, key, func) {
return [...arr.slice(0, key), func(arr[key]), ...arr.slice(key + 1)]
}
function objUpdate(obj, key, func) {
return {...obj, [key]: func(obj[key])}
}
// simplified component
function MyComponent() {
const [headings, setHeadings] = useState(items);
const updateName = key => event => {
setHeadings(prevState =>
// update array at key
updateArr(prevState, key, elem =>
// update elem's name property
updateObj(elem, "name", prevName =>
// new element name
event.currentTarget.value
)
)
)
}
return headings.map((h, key) =>
<input key={key} id={h.id} value={h.name} onChange={updateName(key)} />
)
}
multiple arrow functions?
Curried functions make writing your event handlers easier, but maybe you've never used them before. Here's what the uncurried version would look like -
function MyComponent() {
const [headings, setHeadings] = useState(items);
const updateName = (key, event) => {
setHeadings(prevState =>
// update array at key
updateArr(prevState, key, elem =>
// update elem name property
updateObj(elem, "name", prevName =>
// new element name
event.currentTarget.value
)
)
)
}
return headings.map((h, key) =>
<input key={key} id={h.id} value={h.name} onChange={e => updateName(key, e)} />
)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论