我有一个产品列表,我想添加一些其他数据,例如价格和数量。问题是我开始输入时会失去输入焦点,因为整个列表都在重新呈现。我有一个简单的Fiddle复制该段代码:
const App = () => {
// List of products
const [products, setProducts] = React.useState([
{
title: 'My Product',
},
{
title: 'My Product 2',
}
]);
// Simple debug to track changes
React.useEffect(() => {
console.log('PRODUCTS', products);
}, [products]);
const ProductForm = ({ data, index }) => {
const handleProductChange = (name, value) => {
const allProducts = [...products];
const selectedProduct = {...allProducts[index]};
allProducts[index] = {
...selectedProduct,
[name]: value
};
setProducts([ ...allProducts ]);
}
return (
<li>
<h2>{data.title}</h2>
<label>Price:</label>
<input type="text" value={products[index].price} onChange={(e) => handleProductChange('price', e.target.value)} />
</li>
);
}
return <ul>{products.map((item, index) => <ProductForm key={item.title} index={index} data={item} />)}</ul>;
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
https://jsfiddle.net/lucasbittar/zh1d6y37/23/
我尝试了很多在这里找到的解决方案,但是没有一个能真正代表我的能力。
谢谢!
答案 0 :(得分:1)
解决此问题的一种方法是为<ProductForm />
使用一个单独的组件,然后在其中传递所需的props
。
const ProductForm = ({ data, index, products, setProducts }) => {
const handleProductChange = (name, value) => {
const allProducts = [...products];
const selectedProduct = {...allProducts[index]};
allProducts[index] = {
...selectedProduct,
[name]: value
};
setProducts([ ...allProducts ]);
}
return (
<li>
<h2>{data.title}</h2>
<label>Price:</label>
<input
type="text"
value={products[index].price || ''}
onChange={(e) => handleProductChange('price', e.target.value)}
/>
</li>
);
}
const App = () => {
const [products, setProducts] = React.useState([{title: 'My Product'},{title: 'My Product 2'}]);
return (
<ul>
{products.map((item, index) =>
<ProductForm
key={item.title}
index={index}
data={item}
products={products}
setProducts={setProducts}
/>
)}
</ul>
)
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
这是有效的JSFiddle代码link
答案 1 :(得分:1)
您已经在ProductForm
内部定义了App
,因此在每个渲染周期都会重新创建它,即,每个渲染都是一个全新的组件。
移动ProductForm
以在外部定义。现在出现了以下问题:handleProductChange
无法访问App
的功能范围products
状态。此处的解决方案是将handleProductChange
移回App
并更新函数签名以使用index
。使用data.price
作为输入值,您可以提供后备值或为此属性提供初始状态。
建议:命名输入name="price"
并简单地从事件对象中使用它。
const ProductForm = ({ data, index, handleProductChange }) => {
return (
<li>
<h2>{data.title}</h2>
<label>Price:</label>
<input
name="price" // <-- name input field
type="text"
value={data.price || ''} // <-- use data.price or fallback value
onChange={(e) => handleProductChange(e, index)} // <-- pass event and index
/>
</li>
);
}
const App = () => {
// List of products
const [products, setProducts] = React.useState([
{
title: 'My Product',
},
{
title: 'My Product 2',
}
]);
// Simple debug to track changes
React.useEffect(() => {
console.log('PRODUCTS', products);
}, [products]);
// Update signature to also take index
const handleProductChange = (e, index) => {
const { name, value } = e.target; // <-- destructure name and value
const allProducts = [...products];
const selectedProduct = {...allProducts[index]};
allProducts[index] = {
...selectedProduct,
[name]: value
};
setProducts([ ...allProducts ]);
}
return (
<ul>
{products.map((item, index) => (
<ProductForm
key={item.title}
index={index}
data={item}
handleProductChange={handleProductChange} // <-- pass callback handler
/>)
)}
</ul>
);
}
ReactDOM.render(
<App />,
document.getElementById('container')
);