Hyrule
Hyrule

Reputation: 645

ReactJs How to re-call API after initial render?

I have a Home component with a searchbar.

const Intro = (props) => {

    return (
        <Searchbar 
        onSubmit={props.onSubmit}
        onChange={props.onChange} 
        value={props.value}
        header='Nutrion'>
        </Searchbar>
    )
}

Searchbar is a controlled component. It get's passed event handlers

handleSubmit(e) {
        e.preventDefault()
        this.setState({
            food: this.state.value,
            submitted: true
        })
    }

If the value inside Searchbar get's submitted, it sets statefood: this.state.value and submitted: true.

submitted && <Redirect to={{
           pathname: `/search/results`,
           search: `?food=${food}`,
                        }} />

Submitted true triggers a redirect to Results component, passing along a query string with the submitted food in the searchbar.

   componentDidMount() {
        console.log('hiii')
        const food = this.state.food || queryString.parse(this.props.location.search).food
        fetchRecipes(food).then(recipes => {
            if (recipes === null) {
                return this.setState (
                    {
                        error: 'Server failed to respond. Please try again.',
                        loading: false
                    })
            }

            this.setState( {
                error: null,
                recipes: recipes,
                loading: false,
                isFetched: true
            })
        })

    }

Here is the problem. This is the ComponentDidMount() inside the redirected to class Results. It takes the query string we passed earlier and parses it. The result of this is used for an API request and the returning data is passed to this.state.recipes. Everything works. Data gets passed to the recipeList

{!this.state.loading && <Recipelist recipes={this.state.recipes} />}

But this only works on INITIAL render. If i change this.state.food by submitting the value inside (another) Searchbar inside Results, it doesn't re-request API data and update RecipeList with new Recipes. How can you make a new API request each item a new value is submitted to this.state.food and re-render RecipeList?

I have posted links to relevant files below:

import React from 'react'
import { Redirect } from 'react-router-dom'
import Searchbar from './../ui/Searchbar'

import { 
    Jumbotron,
    // PageHeader
 } from 'react-bootstrap'

const Intro = (props) => {
    
    return (
        <Searchbar 
        onSubmit={props.onSubmit}
        onChange={props.onChange} 
        value={props.value}
        header='Nutrion'>
        </Searchbar>
    )
}

class Home extends React.Component {

    constructor(props) {
        super(props)
        this.state = {
            submitted: false,
            food: '',
            value: ''
        }

        this.handleSubmit = this.handleSubmit.bind(this)
        this.handleChange = this.handleChange.bind(this)
    }
     handleChange(e) {
        this.setState({ value: e.target.value })
    }

    handleSubmit(e) {
        e.preventDefault()
        this.setState({
            food: this.state.value,
            submitted: true
        })
    }

    render() {
        // REMINDER: toegang tot path is via this.props.match => match.url object
        const submitted = this.state.submitted
        const food = this.state.food

        return (
            
                <Jumbotron>
                    {
                        !submitted && 
                        <Intro onSubmit={this.handleSubmit}
                        onChange={this.handleChange}
                        value={this.state.value}/> 
                    }
                    {
                        submitted && 
                        <Redirect to={{
                            pathname: `/search/results`,
                            search: `?food=${food}`,
                        }} />
                    }
                </Jumbotron>
            
        )
    }
}

export default Home

import React, {Component} from 'react'
import {Jumbotron} from 'react-bootstrap'
import Searchbar from './../../ui/Searchbar'
import { fetchRecipes } from './../../utils/api'
import queryString from 'query-string'
import Recipelist from './Recipelist'

class Results extends Component {
    
    constructor(props) {
        super(props)
        this.state = {
            value: '',
            food: '',
            recipes: null,
            isFetched: false,
            error: null,
            loading: true
        }

        this.handleChange = this.handleChange.bind(this)
        this.handleSubmit = this.handleSubmit.bind(this)
    }

    handleChange(e) {
        this.setState({ value: e.target.value })
    }

    componentDidMount() {
        console.log('hiii')
        const food = this.state.food || queryString.parse(this.props.location.search).food
        fetchRecipes(food).then(recipes => {
            if (recipes === null) {
                return this.setState (
                    {
                        error: 'Server failed to respond. Please try again.',
                        loading: false
                    })
            }

            this.setState( {
                error: null,
                recipes: recipes,
                loading: false,
                isFetched: true
            })
        })
        
    }

   
    handleSubmit(e) {
        e.preventDefault()
        this.setState( {
            food: this.state.value
        })
    }

    render(){

        if (this.state.loading) {
            return <p> Loading ... </p>
        }
         if (this.state.error) {
      return (
        <div>
          <p>{this.state.error}</p>
          {/*<Link to='/'>try again</Link>*/}
        </div>
      )
    }
        return (
            <div>
            <Jumbotron>
                <Searchbar
                value={this.state.value}
                onSubmit={this.handleSubmit} 
                onChange={this.handleChange}/>
            </Jumbotron>
             {!this.state.loading && <Recipelist recipes={this.state.recipes} />}
            </div>
        )
    }
}


export default Results

{
  "name": "nutrion",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "normalize-css": "^2.3.1",
    "prop-types": "^15.5.10",
    "query-string": "^4.3.4",
    "react": "^15.5.4",
    "react-bootstrap": "^0.31.0",
    "react-dom": "^15.5.4",
    "react-router-dom": "^4.1.1",
    "semantic-ui-react": "^0.68.5"
  },
  "devDependencies": {
    "react-scripts": "1.0.7"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}

Upvotes: 0

Views: 473

Answers (1)

Garry Taylor
Garry Taylor

Reputation: 1115

You might be able to place a call to fetchRecipes(food) within the handleSubmit

handleSubmit(e) {
    e.preventDefault();
    let recipes = await = fetchRecipes(food);
    let food = e.target.value;
    this.setState( {
        food, recipes
    });
}

Upvotes: 1

Related Questions