Alanaj
Alanaj

Reputation: 247

How do you can I update api result with the value of my input?

I am new to react and I am trying to build an app that let's you call a recipe when you enter in foods in the search box. I have tried to update the query search with the userInput state but it has no effect on the outcome of the api results. I have tried looking at other examples and documentation but it is not solving my problem. Can anyone point me in the right direction?

Here is my code:

class App extends React.Component {

  constructor(props) {
    super(props)
    this.state = {
      userInput: ''
    }
  }

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

  componentDidMount() {

   //I want whatever is typed in the userinput to have results that display in the api
    const { userInput } = this.state
    fetch(`https://api.edamam.com/search?q=${userInput}&app_id=${APP_ID}&app_key=${APP_KEY}`)
      .then(res => {
        console.log(res.json())
      })
  }



  render() {


    return (
      <div className="recipes">
        <Nav changed={this.handleChange} />
        <Content userInput={this.state.userInput} />
      </div>
    )
  }
}


const Nav = (props) => {

    // the nav component is where the input is located. 
    return (
        <nav className="nav">
            <h1 className="title" >Nourish</h1>
            <input type="text" className="input" onChange={props.changed} />
        </nav>
    )

}

Upvotes: 0

Views: 364

Answers (3)

Ioannis Potouridis
Ioannis Potouridis

Reputation: 1316

there are a couple of issues here, for example you need to debounce those requests so they don't happen on every input change. One way you can do this is by keeping a timer property in your App class and resetting it every time userInput's state change. Then fire the request. Here's a simple implementation you can follow.

const Nav = (props) => {
  return (
    <nav>
      {/* Notice that I'm also passing userInput as a value to my input */}
      {/* If you don't know why read about controlled components in React */}
      <input onChange={props.changed} value={props.userInput} />
    </nav>
  )
}

And then your App class.

class App extends React.Component {
  // You can also initialize the state like this
  state = {
    userInput: ''
  }

  // This is the timer you need to reset on every userInput change
  // This has to be a class property and not in your state
  timer = 0;

  componentDidUpdate(_, prevState) {
    // if the userInput has changed
    if (prevState.userInput !== this.state.userInput) {
      // reset the timer
      window.clearTimeout(this.timer);

      // fire the request
      this.timer = window.setTimeout(() => {
        fetch(`...?q=${this.state.userInput}&...`).then(// do whatever you want);
      }}, 500);
    }
  }

  // We don't reset the timeout value here because we don't want to break
  // the user experience
  handleChange = (event) => {
    this.setState({ userInput: event.target.value });
  }

  render() {
    return (
      <div className="recipes">
        <Nav changed={this.handleChange} userInput={this.state.userInput} />
      </div>
    )
  }
}

Upvotes: 0

Cory Harper
Cory Harper

Reputation: 1055

The issue here is where you are doing your API call, in componentDidMount, which is a lifecycle method that only fires one, when the component mounts for the first time.

As for how the implementation should actually be, this is highly debatable because I am unsure if what you describe is actually what you'll want. Making a new request to your API on every key down for you input could be far too many requests.

Disregarding that though, based on your description, move the code that updates the query to the API into the onChange function of your input, this will allow you to update the value being queried whenever the value of the input changes.

Upvotes: 2

Ryuko
Ryuko

Reputation: 377

class App extends React.Component {

  constructor(props) {
    super(props)
    this.state = {
      userInput: ''
    }

    this.typingTimeout = 0;
  }

  handleChange = (e) => {
    const newQuery = e.target.value; 
    
    clearTimeout(this.typingTimeout);
    this.setState({ userInput: newQuery });

    this.typingTimeout = setTimeout(() => {
      if (newQuery === '') return;

      handleLoadData(newQuery);
    }, 500);
  }

  handleLoadData = (uInput) => {
    fetch(`https://api.edamam.com/search?q=${uInput}&app_id=${APP_ID}&app_key=${APP_KEY}`)
      .then(res => {
        console.log(res.json())
      })
  }

  render() {
    return (
      <div className="recipes">
        <Nav changed={this.handleChange} />
        <Content userInput={this.state.userInput} />
      </div>
    )
  }
}


const Nav = (props) => {

    // the nav component is where the input is located. 
    return (
        <nav className="nav">
            <h1 className="title" >Nourish</h1>
            <input type="text" className="input" onChange={props.changed} />
        </nav>
    )

}

The typingTimeout prevents fetching data while the user is typing. Once the user stops typing, it fetches data.

Upvotes: 0

Related Questions