Currently trying to useState with prevState for incrementing and decrementing my counter but it is
\ incrementing and decremeting by 2 each time, even though I only want it to do it once each time.

我已经try 返回updatdProducts[index].count,但是它返回了一个错误,即Products.map不是一个函数,我假设这是因为我不再传递array.

  const [product, setProduct] = useState([])

  const decrementCount = (index) => {
    setProduct((prevState) => {
      const updatedProducts = [...prevState];
      // create min for the count, can not go less than 0
      // math.max() returns the value that is the smallest
      const newCountdown = Math.max(0, updatedProducts[index].count - 1)
      updatedProducts[index].count = newCountdown;
      return updatedProducts
    });
  }

  const incrementCount = (index) => {
    setProduct((prevState) => {
      // const updatedProducts = [...prevState];
      const updatedProducts = [...prevState];
      // create max for the count, can not go more than 10
      const newCount = Math.min(10, updatedProducts[index].count + 1)
      updatedProducts[index].count = newCount;
      // console.log('This is the count'+updatedProducts[index].count)
      return updatedProducts;
    });
  }

  const url = 'http://makeup-api.herokuapp.com/api/v1/products.json?brand=maybelline'

  async function callAPI() {
    try {
      const request = await fetch(url);
      if (request.status === 200) {
        console.log('Success!');
        const response = await request.json();
        // this spreads the items from the response call and adds a count of 1 to each card/image
        const productsWithCount = response.map((item) => ({
          ...item, count: 1

        }));
        setProduct(productsWithCount);
      } else {
        console.log(`Server error: ${request.status}`);
      }
    } catch (err) {
      console.log(`Fetch error: ${err}`);
    }
  }

  return (
    <div>
      <input
        type="text"
        placeholder="Search here" />

      <button onClick={callAPI}>Click to get API</button>
      <div className='shopCards grid grid-cols-5'>
        {
          product.map((item, index) => {
            return (
              <div key={item.id}>
                <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
                  <path stroke-linecap="round" stroke-linejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z" />
                </svg>
                <img src={item.api_featured_image} ></img>
                <div className='addToCart'>
                  <div className='productQuantity'>
                    <button onClick={() => incrementCount(index)} value={item.id}>
                      +
                    </button>
                    <p>{item.count}</p>
                    <button onClick={() => decrementCount(index)}>
                      -
                    </button>
                  </div>
                  <button>
                    Add to cart
                  </button>
                </div>
              </div>
            )
          }

          )
        }

      </div>
    </div >
  )
}

export default Shop

推荐答案

const updatedProducts = [...prevState];是浅表副本,不复制数组中的对象.react 状态应该是不变的,但是线updatedProducts[index].count = newCountdown;Mutations prevState.

try

const updatedProducts = prevState.map(e => ({...e}));

它复制数组内的每个对象以及array.然后,使用=来Mutations 拷贝是安全的.

如果您的对象有嵌套的对象或数组,您也需要复制它们,因为上面的代码只执行每个对象的浅层复制.

此外,<StrictMode>可能会导致双重渲染,因此请判断这是否有干扰.

吹毛求疵:我会用[products, setProducts],因为它是一系列多种产品.

可运行的简化示例,您可以从以下方面进行操作:

const url = "http://makeup-api.herokuapp.com/api/v1/products.json?brand=maybelline";

const Products = () => {
  const [products, setProducts] = React.useState([]);

  const fetchProducts = () => {
   fetch(url)
     .then(res => res.json())
     .then(data =>
       setProducts(data.map(e => ({...e, count: 1})))
     );
  };

  const increment = index => {
    setProducts(prevState => {
      const newState = prevState.map(e => ({...e}));
      newState[index].count++;
      return newState;
    });
  };

  const decrement = index => {
    setProducts(prevState => {
      const newState = prevState.map(e => ({...e}));
      newState[index].count--;
      return newState;
    });
  };

  return (
    <React.StrictMode>
      <button onClick={fetchProducts}>Show products</button>
      <ul>
        {products.map((e, i) => (
          <li key={e.id}>
            <span>{e.name}</span>
            <button onClick={() => increment(i)}>+</button>
            <span>{e.count}</span>
            <button onClick={() => decrement(i)}>-</button>
          </li>
        ))}
      </ul>
    </React.StrictMode>
  );
};

ReactDOM.createRoot(document.querySelector("#app"))
  .render(<Products />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>

我更喜欢按id进行搜索,而不是按索引进行搜索--大多数情况下,您需要向API发送请求以将更改持久化到数据库中,该数据库将基于id而不是索引.

还请注意,如果您将prevState.map切换到[...prevState],上面的片段不一定会重现您的问题,因为Mutations 是可能会误导人的工作方式之一.虽然可能还有另一个因素(我没有看到任何其他明显的问题),但这仍然是一个需要解决的重要问题.

Reactjs相关问答推荐

LocalStore未存储正确的数据

过滤对象数组并通过迭代对象数组将属性放入新数组

防止在Reaction中卸载

react -在选项中 Select 我想要显示和图像.但当我 Select 该选项时,我希望控件仅显示该选项的文本部分

如何在与AntD的react 中限制文件上传和显示消息?

在Map()完成React.js中的多个垃圾API请求后执行函数

React:关于useEffect钩子如何工作的困惑

有没有可能在next.js和ssr中使用tsps?

React-router动态路径

使用jest如何覆盖对象的模拟?我正在使用`jest.mock()`来模拟对象,并希望 for each 测试用例覆盖模拟.

如何将 DocuSign 控制台界面嵌入到 React 应用中

React中useAuthenticator的authState在成功登录后仍保持未认证状态 - 需要强制刷新

即使配置了 webpack.config.js,html-loader 也不起作用

Formik onChange() 字段挂钩覆盖显示值和 Select 机制

在准备回调中使用状态属性(Redux 工具包)

如何从 graphql/apollo 中的缓存中清除多个查询

为什么此代码中的 useState 不更新(文本更新但 id 不更新)?

react 页面更新

如何在 react-router-dom v6 中实现监听功能?

当页面重新加载react 路由导航到第一页 v6