Ethan_J
Ethan_J

Reputation: 65

ReactJS - fetch in async onclick not working

Having trouble getting changes to persist with async function when called from an onClick event handler. I have the functions I am calling:

        retrieveData = async () =>{
        try {
            console.log(1)
            console.log(this.state.ID)
            const url = this.baseUrl + "api/" + this.state.ID
            let response = await fetch(url, {mode: "cors"})
            const data = await response.text()
            this.setState({testVal: data})
            console.log(data)
        } catch (e) {
            console.error(e)
        }
    }

The above function works when I put its contents in componentDidMount() and hardcode the state ID. But not when I try to call the retrieved data function like so, so I assume the following snippet is where the error is.

    render() {
        return(
            <div>
                <div>
                    <form>
                        <label>
                            ID:
                            <input type="text" onChange={e => this.setState({ID: e.target.value})}/>
                        </label>
                        <input type="submit" onClick={async () =>{await this.retrieveData();}}/>
                    </form>
                </div>
                <div>
                    {this.state.ID}
                    {this.state.testVal}
                </div>
            </div>
        );
    }

Im fairly new to reactJS, so I am sure it is something simple. When I click the submit button, I see the console spit out the 1, and then the ID I had just entered, and then both immediately disappear. When I check to see if the fecth is happening, I dont see any calls to the api like when I tested with a hardcoded url with componentDidMount(), but I want to be able to take user input, just a little unsure how to. I believe I have checked just about every SO question I can find on this, so I really am lost. If more info is needed please let me know, thank you.

Upvotes: 1

Views: 6885

Answers (3)

Ethan_J
Ethan_J

Reputation: 65

Thank you all for your help, this is the following change I made.

I changed

<input type="submit" onClick={async () =>{await this.retrieveData();}}/>

To this

<input type="button" value= "submit" onClick={e=>this.retrieveData(e)}/>

And now it works perfectly, so no issues with that. Thank you again.

Upvotes: 2

Alexander Staroselsky
Alexander Staroselsky

Reputation: 38757

The issue is that the form submit handler is being executed and that is refreshing the page as there is not prevent default to catch that submit event. To resolve this I'd recommend to move the handler from the input button to the form handler. You would also need to prevent the form submit from occurring which you can achieve via the form handler event and preventDefault(). Otherwise the page would refresh when you submit the form.

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

retrieveData = async (e) =>{
  e.preventDefault();

  try {
      console.log(1)
      console.log(this.state.ID)
      const url = this.baseUrl + "api/" + this.state.ID
      let response = await fetch(url, {mode: "cors"})
      const data = await response.text()
      this.setState({testVal: data})
      console.log(data)
    } catch (e) {
      console.error(e)
    }
}

render() {
  return(
      <div>
          <div>
              <form onSubmit={this.retrieveData}>
                  <label>
                      ID:
                      <input type="text" handleChange={this.handleChange} />
                  </label>
                  <button type="submit">Submit</button>
              </form>
          </div>
          <div>
              {this.state.ID}
              {this.state.testVal}
          </div>
      </div>
  );
}

Notice I removed the click handler from the input submit control, because I'd imagine that two events were occurring a click on the button then also a submit on the form. While the input submit control click was occurring, the submit on the form itself was not being prevent and causing the page to refresh and preventing the HTTP request from completing.

Hopefully that helps!

Upvotes: 0

Khabir
Khabir

Reputation: 5854

Please check this example where you call api using fetch after clicking a button.

import React, {Component} from "react";

class FetchExample extends React.Component {
    state = {
        isLoading: false,
        questions: [],
        error: null
    };

    fetchQuestions = () => {
        fetch(`https://opentdb.com/api.php?amount=10&difficulty=hard&type=boolean`,)
            .then(response => {
                    if (response.status !== 200) {
                        console.log('There was a problem. Status Code: ' + response.status);
                        return;
                    }
                    response.json().then(data => {
                        console.log(data);
                        this.setState({
                            questions: data,
                            isLoading: false
                        })
                    });
                }
            )
            .catch(function (error) {
                console.log('Error: ', error);
                this.setState({error, isLoading: false})
            });
    };

    render() {
        const {isLoading, questions, error} = this.state;
        return (
            <React.Fragment>
                <h1>Random Question</h1>
                <button onClick={this.fetchQuestions}>Click for calling API using fetch</button>
                {error ? <p>{error.message}</p> : null}

                {!isLoading && questions.results ? (
                    questions.results.map((questions, index) => {    //something right here
                        //is erroring
                        const {question, category, type, difficulty} = questions;
                        return (
                            <div key={index}>
                                <p>Question: {question}</p>
                                <p>Question Type: {type}</p>
                                <p>Difficulty: {difficulty}</p>
                                <hr/>
                            </div>
                        );
                    })
                ) : isLoading ? (
                    <h3>Loading...</h3>
                ) : null}
            </React.Fragment>
        );
    }
}

export default FetchExample;

Upvotes: 0

Related Questions