0

How do I add a click handler to refetch data from my API based on my input ON CLICK?

In my console I'm getting back data if I input "Jon Snow" for instance because the onChange set to e.target.value but not sure how to fetch this on button click.

Code Sandbox: https://codesandbox.io/s/pedantic-lichterman-4ev6f?file=/src/game.jsx

import React, { useEffect, useState } from "react";
import axios from "axios";

export default function Game() {
  const [error, setError] = useState(null);
  const [name, setName] = useState("");

  const handleSubmit = e => {
    e.preventDefault();
    console.log( name );
  }
  const handleClick = e => {
    // ??
  }

  useEffect(() => {
    fetch(`https://anapioficeandfire.com/api/characters?name=${name}`)
      .then((res) => res.json())
      .then((data) => {
        console.log(data[0].name); // the data I want back
      })
      .catch((error) => {
        console.log("Error", error);
        setError(error);
      });
  }, [name]);

  return (
    
      <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Name"
      />
      <input type="submit" value="Submit" onClick={handleClick}/>
    </form>
  );
}
5
  • Are you want to call the api while clicking on the button? Commented Mar 9, 2021 at 5:39
  • i would like to pass the input value as the "name" param in the url Commented Mar 9, 2021 at 5:41
  • Are you want to call the api onChanging name or on Submitting the value? Commented Mar 9, 2021 at 5:42
  • when i click the button ideally Commented Mar 9, 2021 at 5:43
  • Initial state with name "", do you want to call the api? Commented Mar 9, 2021 at 5:45

4 Answers 4

2

When the Submit button is clicked it will trigger onSubmit event, no need for you to handle the onClick event separately.

import React, { useEffect, useState } from "react";
import axios from "axios";

export default function Game() {
  const [error, setError] = useState(null);
  const [name, setName] = useState("");

  const handleSubmit = e => {
    e.preventDefault();
    console.log( name );
    fetchData(name);
  }
  
  const fetchData = (name) => {
    fetch(`https://anapioficeandfire.com/api/characters?name=${name}`)
      .then((res) => res.json())
      .then((data) => {
        console.log(data[0].name); // the data I want back
      })
      .catch((error) => {
        console.log("Error", error);
        setError(error);
      });
  }

  useEffect(() => {
    fetchData(name);
  }, []);

  return (
    
      <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Name"
      />
      <input type="submit" value="Submit" onClick={handleClick}/>
    </form>
  );
}
Sign up to request clarification or add additional context in comments.

2 Comments

thank you, how would i print the returned API data to my page?
You can create a new state and update it in the then block
1

Add another stateful variable. You need not only a value and setter for the input value but also a value and setter for the API results you want to be able to use elsewhere. Maybe something like

const [searchText, setSearchText] = useState('');
const [result, setResult] = useState('');
// inside fetch callback:
setResult(data[0]?.name ?? ''); // use optional chaining to not throw an error
// if there is no result
<input
  type="text"
  value={searchText}
  onChange={(e) => setSearchText(e.target.value)}
  placeholder="Name"
/>

And then you can use the result where you need.

Live demo:

const App = () => {
  const [error, setError] = React.useState(null);
  const [searchText, setSearchText] = React.useState('');
  const [result, setResult] = React.useState('');

  const handleSubmit = e => {
    e.preventDefault();
    console.log( name );
  }

  React.useEffect(() => {
    fetch(`https://anapioficeandfire.com/api/characters?name=${searchText}`)
      .then((res) => res.json())
      .then((data) => {
        setResult(data[0] ? data[0].name : '');
      })
      .catch((error) => {
        console.log("Error", error);
        setError(error);
      });
  }, [searchText]);
  console.log(result);

  return (
    
      <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={searchText}
        onChange={(e) => setSearchText(e.target.value)}
        placeholder="Name"
      />
      <input type="submit" value="Submit" onClick={e => e.preventDefault()}/>
    </form>
  );
}

ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>

4 Comments

where does the callback go?
The same place where you're using data[0].name currently. Just replace that line with the new setResult(data[0]?.name ?? '');
i think i’m lost. didn’t work for me. did what you said
It looks to work for me, see snippet. But the API you're using has problems, it rarely returns array items from user input. See anapioficeandfire.com/api/characters it looks like all the names are empty. Find a better API.
0

You can use direct state variable [name] in handleClick function.

Comments

0

The other answers are all correct that you should trigger the fetch in your handleSubmit. I just wanted to chime in with some sample code for rendering results since you asked for help with that.

The API returns an array of characters. We want to map through that result and show each character. We also want to tell the user if there were no results (especially since this API seems to only work with an exact name and will return a result for "Arya Stark" but not for "Stark"). We don't want to show that "No Characters Found" message before they have submitted.

I am using a setState hook to store the array of character matches from the API. I am initializing the state to undefined instead of [] so that we only show the no results message if it gets set to [].

My code allows the user to submit multiple times. We keep displaying the previous results until they submit a new search. Once we have an array in our characters state, we display those results.

// an example component to render a result
const RenderCharacter = ({ name, aliases }) => {
  return (
    <div>
      <h2>{name}</h2>
      {aliases.length && (
        <div>
          <h3>Aliases</h3>
          <ul>
            {aliases.map((a) => (
              <li key={a}>{a}</li>
            ))}
          </ul>
        </div>
      )}
    </div>
  );
};
export default function Game() {
  // current form input
  const [name, setName] = useState("");
  // save characters returned from the API
  // start with undefined instead of empty array
  // so we know when to show "no characters found" message
  const [characters, setCharacters] = useState();
  // store API errors
  const [error, setError] = useState(null);

  const fetchData = () => {
    fetch(`https://anapioficeandfire.com/api/characters?name=${name}`)
      .then((res) => res.json())
      .then(setCharacters) // store data to state
      .then(() => setError(null)) // clear previous errors
      .catch((error) => {
        console.log("Error", error);
        setError(error);
        setCharacters(undefined); // clear previous character matches
      });
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    fetchData();
  };

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input
          type="text"
          value={name}
          onChange={(e) => setName(e.target.value)}
          placeholder="Name"
        />
        <input type="submit" value="Submit" />
      </form>
      {characters !== undefined &&
        (characters.length === 0 ? (
          <div>No Characters Found</div>
        ) : (
          <div>
            {characters.map((character) => (
              <RenderCharacter key={character.name} {...character} />
            ))}
          </div>
        ))}
      {error !== null && <div>Error: {error.message}</div>}
    </div>
  );
}

Code Sandbox Demo (with typescript annotations)

Comments

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.