Youngjae
Youngjae

Reputation: 47

Map doesn't work in render after fetch in React JS

I'm struggling with fetching data and render to the screen in React JS

class Home extends Component{
    componentWillMount(){
        foods=[];
        fetch('http://192.249.19.243:0280/main/get_recipes')
            .then(res => res.json())
            .then(data => foodlist=data)
            .then(
                () => console.log("f:",foodlist),
            )
            .then(
                () => {foodlist.map(item => foods.push({title:item, img:"http://192.249.19.243:0280/main/image/"+item}));
                    console.log("foods", foods);
                    this.render();
                }
        );
}
componentDidMount(){
}

render(){
    console.log("render in!");
    return (
        <div>
            <ul>
                {
                    console.log(foods), // this works fine -> 4 elements
                    foods.length!=0 ?
                    foods.map(item=>
                        <Item 
                            title={item.title} 
                            img={item.img}/>
                        )
                    :
                        <p id="loadingMsg">Data Loading...</p>
                }
            </ul>
        </div>
        );
    }
}
export default Home;

in the render(), I checked console.log(foods) print 4 elements, but Nothing appears in the screen..

I don't know why.. Please help me..

Upvotes: 0

Views: 1303

Answers (3)

Omar Sy
Omar Sy

Reputation: 496

In react: it is not you who manage the render. If you want to render an element you need to call this.setState with the data that changed. You can see my example:

class Home extends Component{
state = { 
foods: []
}
    componentWillMount(){
        fetch('http://192.249.19.243:0280/main/get_recipes')
            .then(res => res.json())
            .then(data => foodlist=data)
            .then(
                () => console.log("f:",foodlist),
            )
            .then(
                () => {
this.setState({foods: foodlist.map(item => ({title:item, img:"http://192.249.19.243:0280/main/image/"+item})));
                }
        );
}
componentDidMount(){
}

render(){
    console.log("render in!");
    return (
        <div>
            <ul>
                {
                    this.state.foods.length!=0 ?
                    this.state.foods.map(item=>
                        <Item 
                            title={item.title} 
                            img={item.img}/>
                        )
                    :
                        <p id="loadingMsg">Data Loading...</p>
                }
            </ul>
        </div>
        );
    }
}
export default Home;

Upvotes: 2

brijesh-pant
brijesh-pant

Reputation: 1145

Thats not the correct way to handle data in a react component. You should maintain list of foods in component state. Code sandbox: https://codesandbox.io/s/falling-bush-b9b78 As an example

import React from "react";

export default class App extends React.Component {
  constructor() {
    super();
    this.state = {
      foods: []
    };
  }
  componentDidMount() {
    const fetchMock = url =>
      new Promise(resolve => {
        setTimeout(() => resolve(["Barley", "Chicken", "Oats"]), 2000);
      });
    fetchMock("http://192.249.19.243:0280/main/get_recipes").then(foods => {
      this.setState({
        foods
      });
    });
  }

  render() {
    console.log("render in!");
    const { foods } = this.state;
    return (
      <div>
        <ul>
          {foods.length !== 0 ? (
            foods.map(food => <h1 key={food}>{food}</h1>)
          ) : (
            <p id="loadingMsg">Data Loading...</p>
          )}
        </ul>
      </div>
    );
  }
}

Upvotes: 0

GBourke
GBourke

Reputation: 1963

It looks like you are relatively new to React. I spot quite a few errors with this.

Please read the docs on class based components carefully

I have tried to refactor it without context. Give it a bash

class Home extends Component {

  //initialize state in the constructor for class based components
  constructor(props) {
    super(props);
    //foods must be an empty array otherwise .length may fail
    this.state = { foods: [] }
  };

  //once the component has mounted, call the method which will perform the fetch
  componentDidMount() {
    this.fetchFoodData();
  }

  //calls the endpoint which returns a promise. The promise will then set the components state, which will trigger a render
  fetchFoodData = () => {
    fetch('http://192.249.19.243:0280/main/get_recipes')
      .then(res => {
        const foodData = res.json();
        //not sure what your body looks like, but foods should be an array containing your food objects
        const foods = foodData.map(item => foods.push({ title: item, img: "http://192.249.19.243:0280/main/image/" + item}));
        //calling setState will cause react to call the render method.
        this.setState({ foods: foods })
      }).catch(err => {
        //handle errors here
        console.log(err);
      });
  };
  //React calls this method when props or state change for this component
  render() {
    return (
      <div>
        <ul>
          {
            foods.length != 0 ?
              foods.map(item =>
                <Item
                  title={item.title}
                  img={item.img} />
              )
              :
              <p id="loadingMsg">Data Loading...</p>
          }
        </ul>
      </div>
    );
  }
}
export default Home;

Upvotes: 0

Related Questions