更改单个项目时重新渲染整个列表

时间:2020-07-09 19:17:39

标签: reactjs

我有一个产品列表,我想添加一些其他数据,例如价格和数量。问题是我开始输入时会失去输入焦点,因为整个列表都在重新呈现。我有一个简单的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/

我尝试了很多在这里找到的解决方案,但是没有一个能真正代表我的能力。

谢谢!

2 个答案:

答案 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')
);

Working jsfiddle demo

相关问题