如何在不重新呈现未删除项目的情况下从列表中删除项目?
How can I delete an item from list without re-rendering undeleted Items?
感谢您的所有评论和有益的评论。
按照您的建议,请在下面找到一个代码沙箱 link
https://codesandbox.io/s/magical-butterfly-uk0fjq?file=/src/item.js
这可能会帮助您解决问题。
console
中的一切都很明显,有多个日志,考虑到示例的当前规模,这不会造成任何性能问题,但如果有很长的项目列表,这可能会造成影响。
所以问题是,尽管父级 (ItemList
) 重新呈现,但如何继续防止未删除的项目重新呈现。
console
显示 Item
渲染。
如前所述,我使用了useMemo
+ useCallback
组合,但结果证明不稳定。
希望这个例子能有所帮助并且更明确。
编辑
关于 console
log
,奇怪的是,沙箱示例记录了 2 次 App
、2 次 ItemList
和 12 次 Item
,而在它只记录 1-1-6 次的计算机
更新状态时需要先传播之前的状态再更新
示例:
const deleteItem = (newItem) => {
const newItemList = items.filter(item => item.reference !== newItem.reference);
setItems(prev => (...prev, newItemList));
}
更新状态时需要先传播之前的状态再更新
并且取决于您的类型,您必须 return 数组或对象
spreat 运算符打开了先前的数组或对象,然后您添加了新数据
setItems(prev => [...prev, newItemList])
它听起来就像您想遍历对象数组以在table中生成数据行,每行中的最后一个单元格是“删除”按钮,删除该行。你可以做到。不需要 useEffect
.
const { useState } = React;
// Pass in the data
function Example({ data }) {
// Set the state with the data
const [ tableData, setTableData ] = useState(data);
// When a remove button is clicked `filter`
// out those rows that don't match the row id
// and reset the state
function handleRemove(e) {
const { id } = e.target.closest('tr').dataset;
const filtered = tableData.filter(obj => {
return obj.name !== id;
});
setTableData(filtered);
}
// Create some rows by mapping over the
// table data
return (
<table>
{tableData.map(row => {
return (
<Row
key={row.name}
row={row}
handleRemove={handleRemove}
/>
);
})}
</table>
);
}
// Create a table row and populate the cells
// with the information from the row object
function Row({ row, handleRemove }) {
return (
<tr data-id={row.name}>
<td>{row.name}</td>
<td>{row.reference}</td>
<td>{row.description}</td>
<td>
<button
onClick={handleRemove}
>Remove
</button>
</td>
</tr>
);
}
const data = [
{name: "NAME 1", reference: "REF 1", description: "LOREM IPSUM"},
{name: "NAME 2", reference: "REF 2", description: "LOREM IPSUM"},
{name: "NAME 3", reference: "REF 3", description: "LOREM IPSUM"},
{name: "NAME 4", reference: "REF 4", description: "LOREM IPSUM"},
{name: "NAME 5", reference: "REF 5", description: "LOREM IPSUM"}
];
ReactDOM.render(
<Example data={data} />,
document.getElementById('react')
);
table { border-collapse: collapse; border: 1px solid #565656; }
td { border: 1px solid #dfdfdf; padding: 0.5em;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
所以,如果你想最小化项目的 re-renders,你需要确定一些事情
记忆组件
你可以在你想要的组件上使用React.memo
,当props/state不改变时,这将防止re-renders。
所以,使用
export default React.memo(Item);
而不是
export default Item;
确保道具相同
渲染 Item
组件时,您将 deleteItem
作为道具传递,其值是从 App
组件接收的。但是,这个函数并不稳定(每次 App 组件渲染时都会重新定义。并且由于 App
保持 items
状态,因此每次删除后都会重新渲染。这将触发新的 deleteItem
被定义,这将导致 Item
到 re-render.
要使其稳定,您需要做两件事。
- 使用
React.useCallback
re-uses 在其依赖项保持不变的情况下具有相同的功能。
- 使用
setItems
的function
form
所以,而不是
const deleteItem = (newItem) => {
const newItemList = items.filter(
(item) => item.reference !== newItem.reference
);
setItems(newItemList);
};
使用
const deleteItem = React.useCallback((itemToDelete) => {
setItems((currentItems) =>
currentItems.filter((item) => item.reference !== itemToDelete.reference)
);
}, []);
你的代码也有问题,你 .map
数据但是然后在 returning 每个 Item
之前你将它推入一个数组并且 return那反而。只是 return <Item ..>
所以不用
{props.data.map((item, index) => {
const newList = [];
newList.push(
<Item
deleteItem={props.deleteItem}
key={item.reference}
item={item}
></Item>
);
return newList;
})}
做
{props.data.map((item, index) => {
return (
<Item
deleteItem={props.deleteItem}
key={item.reference}
item={item}
></Item>
);
})}
已更新的代码和框包含所有更改:https://codesandbox.io/s/twilight-water-9iyobx
感谢您的所有评论和有益的评论。 按照您的建议,请在下面找到一个代码沙箱 link
https://codesandbox.io/s/magical-butterfly-uk0fjq?file=/src/item.js
这可能会帮助您解决问题。
console
中的一切都很明显,有多个日志,考虑到示例的当前规模,这不会造成任何性能问题,但如果有很长的项目列表,这可能会造成影响。
所以问题是,尽管父级 (ItemList
) 重新呈现,但如何继续防止未删除的项目重新呈现。
console
显示 Item
渲染。
如前所述,我使用了useMemo
+ useCallback
组合,但结果证明不稳定。
希望这个例子能有所帮助并且更明确。
编辑
关于 console
log
,奇怪的是,沙箱示例记录了 2 次 App
、2 次 ItemList
和 12 次 Item
,而在它只记录 1-1-6 次的计算机
更新状态时需要先传播之前的状态再更新 示例:
const deleteItem = (newItem) => {
const newItemList = items.filter(item => item.reference !== newItem.reference);
setItems(prev => (...prev, newItemList));
}
更新状态时需要先传播之前的状态再更新
并且取决于您的类型,您必须 return 数组或对象 spreat 运算符打开了先前的数组或对象,然后您添加了新数据 setItems(prev => [...prev, newItemList])
它听起来就像您想遍历对象数组以在table中生成数据行,每行中的最后一个单元格是“删除”按钮,删除该行。你可以做到。不需要 useEffect
.
const { useState } = React;
// Pass in the data
function Example({ data }) {
// Set the state with the data
const [ tableData, setTableData ] = useState(data);
// When a remove button is clicked `filter`
// out those rows that don't match the row id
// and reset the state
function handleRemove(e) {
const { id } = e.target.closest('tr').dataset;
const filtered = tableData.filter(obj => {
return obj.name !== id;
});
setTableData(filtered);
}
// Create some rows by mapping over the
// table data
return (
<table>
{tableData.map(row => {
return (
<Row
key={row.name}
row={row}
handleRemove={handleRemove}
/>
);
})}
</table>
);
}
// Create a table row and populate the cells
// with the information from the row object
function Row({ row, handleRemove }) {
return (
<tr data-id={row.name}>
<td>{row.name}</td>
<td>{row.reference}</td>
<td>{row.description}</td>
<td>
<button
onClick={handleRemove}
>Remove
</button>
</td>
</tr>
);
}
const data = [
{name: "NAME 1", reference: "REF 1", description: "LOREM IPSUM"},
{name: "NAME 2", reference: "REF 2", description: "LOREM IPSUM"},
{name: "NAME 3", reference: "REF 3", description: "LOREM IPSUM"},
{name: "NAME 4", reference: "REF 4", description: "LOREM IPSUM"},
{name: "NAME 5", reference: "REF 5", description: "LOREM IPSUM"}
];
ReactDOM.render(
<Example data={data} />,
document.getElementById('react')
);
table { border-collapse: collapse; border: 1px solid #565656; }
td { border: 1px solid #dfdfdf; padding: 0.5em;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
所以,如果你想最小化项目的 re-renders,你需要确定一些事情
记忆组件
你可以在你想要的组件上使用React.memo
,当props/state不改变时,这将防止re-renders。
所以,使用
export default React.memo(Item);
而不是
export default Item;
确保道具相同
渲染 Item
组件时,您将 deleteItem
作为道具传递,其值是从 App
组件接收的。但是,这个函数并不稳定(每次 App 组件渲染时都会重新定义。并且由于 App
保持 items
状态,因此每次删除后都会重新渲染。这将触发新的 deleteItem
被定义,这将导致 Item
到 re-render.
要使其稳定,您需要做两件事。
- 使用
React.useCallback
re-uses 在其依赖项保持不变的情况下具有相同的功能。 - 使用
setItems
的function
form
所以,而不是
const deleteItem = (newItem) => {
const newItemList = items.filter(
(item) => item.reference !== newItem.reference
);
setItems(newItemList);
};
使用
const deleteItem = React.useCallback((itemToDelete) => {
setItems((currentItems) =>
currentItems.filter((item) => item.reference !== itemToDelete.reference)
);
}, []);
你的代码也有问题,你 .map
数据但是然后在 returning 每个 Item
之前你将它推入一个数组并且 return那反而。只是 return <Item ..>
所以不用
{props.data.map((item, index) => {
const newList = [];
newList.push(
<Item
deleteItem={props.deleteItem}
key={item.reference}
item={item}
></Item>
);
return newList;
})}
做
{props.data.map((item, index) => {
return (
<Item
deleteItem={props.deleteItem}
key={item.reference}
item={item}
></Item>
);
})}
已更新的代码和框包含所有更改:https://codesandbox.io/s/twilight-water-9iyobx