Search code examples
reactjsjsxes6-promiseethereum

Objects are not valid as a React child when setState of an array at the end of a promise chain


I have a promise chain that creates an array. At the end of the chain, I want to copy that array to one of my state variables. However, I get "Objects are not valid as a React child"

I've tried various ways to chain the promise so that the state variable captures the array I want to put in it. If I put setState at the end of my function, it misses what I try to capture in the promise chain

addIPFSItem = () => {

   var finalItems = []
   var searchAddress = "0x9Cf0dc46F259542A966032c01DD30B8D1c310e05";

   const contract = require('truffle-contract')
   const simpleStorage = contract(SimpleStorageContract)
   simpleStorage.setProvider(this.state.web3.currentProvider)

   this.state.web3.eth.getAccounts((error, accounts) => {
     simpleStorage.deployed().then((instance) => {
       this.simpleStorageInstance = instance

       return this.simpleStorageInstance.getLength(searchAddress);
     }).then((accountLength) => {

        var movieItems = []


        var i;
       //WITHIN THIS LOOP extend the chain to add to movies
        for (i = 0; i < accountLength; i++) {

        var p = this.simpleStorageInstance.getBook(searchAddress, i, { from: searchAddress }).then((hashVal) => {
        return hashVal;
      })
      movieItems.push(p)
    }

    //return items
    return movieItems
  }).then((temp) =>{

    var i;
    var indexCounter=0;
    var arrayLength;
    arrayLength=temp.length

    for(i=0; i<arrayLength; i++){
      var p = temp[i].then((temp)=>{

        var ipfsPrefix = "https://ipfs.io/ipfs/";
        var ipfsURL = ipfsPrefix + temp;

        var movieItem = {id: indexCounter, poster_src: ipfsURL, title: "Some Title", overview: "blah blah"}
        indexCounter++

        return movieItem;
      }).then((item)=>{
        finalItems.push(item)
      }).then(()=>{
        if(finalItems.length == arrayLength ){

          //*******************************
          //Here is where I try to set state and get the error
          //*******************************
          this.setState({rows: finalItems})
        }
      })
    }

    return
  })
})

}

I expect my row in my state to change, but I get Objects are not valid as a React child

UPDATE: here is my render() function

render() {

//Shows customer their account
var userAccount = "Your account is: " + this.state.account;

//Shows current IPFS _address
var currIPFS = "The IPFS address is: " + this.state.ipfsHash;


return (
  <div className="App">

    <table className="titleBar">
    <tbody>

      <h1>Interactive News</h1>
    </tbody>

    </table>

    <input style={{
      fontSize: 14,
      display: 'block',
      paddingTop: 8,
      paddingBottom: 8,
      paddingLeft: 14,
      width: "99%"
    }} onChange={this.searchChangeHandler} placeholder="Enter address for item lookup" />

 {this.state.rows}

    <main className="container">
      <div className="pure-g">
        <div className="pure-u-1-1">
          <h1>Your Image</h1>
          <p>This image is stored on IPFS & The Ethereum Blockchain!!!</p>
          <br />

          <font size="5">
          <span className="badge badge-info" dangerouslySetInnerHTML={{__html: userAccount}} />


          <br />
          <br />

          <span className="badge badge-light" dangerouslySetInnerHTML={{__html: currIPFS}} />

          <br />
          </font>

        <br />
        <br />

          <button onClick={this.addIPFSItem}
            className="btn btn-info btn-sm m-1">ShowList</button>


          <br />
          <br />

          <button onClick={this.handleFirst}
          className="btn btn-info btn-sm m-1">First</button>

          <button onClick={this.handleDecrement}
          className="btn btn-primary btn-sm m-1"> Prev </button>

          <font size="5">
          <span className="badge badge-success">
            {this.state.index}
          </span>
          </font>

          <button onClick={this.handleIncrement}
          className="btn btn-primary btn-sm m-1">Next</button>

          <button onClick={this.handleLast}
          className="btn btn-info btn-sm m-1">Last</button>

          <br/>


          <img src={`https://ipfs.io/ipfs/${this.state.ipfsHash}`} alt=""/>
          <h2>Upload Image</h2>
          <form onSubmit={this.onSubmit} >
            <input type='file' onChange={this.captureFile} />

            <input type='submit' />
          </form>
        </div>
      </div>
    </main>
  </div>
);
}

I'm not sure if my render() function is okay. Note that when I press the button <button onClick={this.addIPFSItem} className="btn btn-info btn-sm m-1">ShowList</button>, that calls the addIPFSItem() function. I don't know if I need a componentDidMount() as it happens after the initial rendering.


Solution

  • React cannot render objects directly. You need to return it as react DOM components. Since 'this.state.rows' is an array of object you need to loop through it and wrap each object inside a meaningful DOM component eg. li or div ..

    <ul>
    {
     this.state.rows.map((row, index) => {
      return (
           <li key={index}>{row.title}</li>
       )
     })
    }
    </ul>
    
    
    
    <main className="container">
          <div className="pure-g">