Skip to content

Update state array in React JS with item from Smart Contract

I have a state array in react which I am trying to add items from a smart contract

The state array is initialised as

const [products, setProducts] = useState([]);

  1. and 2. below did not work, but 3. does work when the array is set outside the loop

What am I doing wrong in 1. and 2?

——————————————————————

  1. did not work
  for (var i = 1; i <= productCount; i++) {
        const product = await marketplace.methods.products(i).call()
        setProducts({
          products: [...this.state.products, product]
        })
      }

——————————————————————

  1. did not work
  for (var i = 1; i <= productCount; i++) {
        const product = await marketplace.methods.products(i).call()
         setProducts(prevState => ({
            products: [...prevState.products, product]
         }))
      }

——————————————————————

  1. worked
  let productArray =[]
  for (var i = 1; i <= productCount; i++) {
        const product = await marketplace.methods.products(i).call()
        productArray.push[product]
      }
  setProduct(productArray)

——————————————————————

Answer

You’re using a functional component, not a class component, so you shouldn’t be using this or this.state – that’s why (1) doesn’t work.

(2) doesn’t work because your initial products are a plain array – it’s not an object with a products property:

const [products, setProducts] = useState([]);

so

     setProducts(prevState => ({
        products: [...prevState.products, product]
     }))

doesn’t make sense – prevState is an array, not an object with a products property.

With a small tweak, (2) could be made to work:

     setProducts(prevState => ([...prevState, product]));

There’s also almost certainly no need to use .call, and you can speed up the process by using Promise.all instead of waiting for each function to finish individually:

Promise.all(
  Array.from(
    { length: productCount },
    (_, i) => marketplace.methods.products(i + 1)()
  )
)
  .then((newProducts) => {
    setProducts([...products, ...newProducts]);
  })
  // .catch(handleErrors);

That’s the version I’d prefer, if your code permits you to make all the requests at once.