2

I'm working with a small application, where I am using react.js. Since I am a beginner I have some issues with it.

I want to map my array, but it won't work. I followed a youtube tutorial and the difference is that the array is populated with hardcoded data. I'm fetching my data and add it into the array when the fetch is complete. (JSON objects).

import React from "react";
import Product from "./Product"

export default class ProductList extends React.Component{
render()
{ console.log("productlist")

    return (
     <ul>
     {this.props.products.map((product, i) => 
        { return <Product product={product} key={i}/>  })}
        {console.log("test")}
    </ul> 
     )

}
}

The console.logs works, but the map-function does not. I have tried without the Id, and in different ways but it won't work. I want the data to pass into my last component, called Product. There are my < li> elements I want to get populated.

When I just passed the "this.props.products" in to "" then I reached the Product component, but no population of data. import React from "react";

export default class Product extends React.Component{
render(){
    console.log("product")

    return (   
  <li> {this.props.product.title} {this.props.product.price} <img src={this.props.product.imageUrl}/> </li>
    )
}
}

I'm using a fetch method to get data from a Json-file.

fetch('http://localhost:8080/products')
.then(
response =>
  response.ok
    ? response.json()
    : Promise.reject(Can't communicate with REST API server (${response.statusText})`),
)
.then(products => {
    products.forEach(product => productArray.push(product));
})

As you can see above I'm adding the products from the Jsonfile into an array I've created. And I am not sure if that is the thing that causes my problem.

import React from "react";
import ReactDOM from "react-dom";
import Container from "./Components/Container";

let productArray = []; 
const app = document.getElementById('app');
ReactDOM.render(<Container products={productArray}/>, app)

I am passing my array into my Container.js file using the ReactDOM.render. And inside of the Container.js file, I also have import React from react and import ProductList from ./Productlist. So I continue to pass the array (and I have checked if there are values in it, and there is.

  <ProductList products = {this.props.products}/>

2 Answers 2

3

It happens because products is outdated by the time your fetch request finishes. And even though you are updating it (by productArray.push) your component doesn't know about such change. Not to mention that this is a bad pattern.

For that, use proper React component lifecycle methods and state handling.

In your Container component:

const Container extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      products: [], // Initialize product as an empty array
    }
  }

  componentDidMount() {
    fetch('http://localhost:8080/products')
      .then(response =>
        response.ok
          ? response.json()
          : Promise.reject(`Can't communicate with REST API server (${response.statusText})`),
      )
      .then(products => {
        this.setState({ products }) // Notify your component that products have been fetched
      })
  }

  render() {
    return (
      <div>
         ...
        <ProductList products={this.state.products} />
      </div>
    )
  }
}

Please not that setState may be asynchronous. Should you fetch new data in the future, don't hesitate to use such feature as well pointed by @Luke M Willis:

this.setState(prevState => ({
  products: [ ...prevState.products, ...products ],
}))
Sign up to request clarification or add additional context in comments.

6 Comments

To append the newly fetched products to state, use functional setState: .then(products => this.setState(prevState => ({ products: [ ...prevState.products, ...products ] })))
By looking at his code, it doesn't look like this request is going to run more than once. Also, I'd use redux for that. I'm not a big fan of managing state directly in the component :)
From the example, I agree, but we don't see where fetch is currently being called. It might be happening in response to a click or some other mechanism. Also, I agree that this state would be appropriate in redux--I do think there is a case for using component state in an app using redux, though. Also, redux can overcomplicate the learning process when getting started with React.
I totally agree with you. That's why I didn't mention redux at all in my answer. I can point that setState is also async, no problem :D
Constructor initialises state so you can update it later via this.setState(...)
|
1

@MissAndersson Where are you calling your fetch function?

If you're calling your fetch function outside of your React components, that might be the issue. Your React components aren't getting re-rendered correctly.

React components get re-rendered whenever their state or props change. See this CodeSandbox example. (https://codesandbox.io/s/812yv7wjrl)

My fetch function updates the state of the component, which forces a re-render.

1 Comment

I am calling the fetch function outside my components. The fetch function is in a index.js file where I use the "reactdom.render(Container....)" and creating the array. I'll try to move it. Thanks for tips

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.