2

So I was trying to fetch some anime data based on the options I have in select (genre), the problem is that the fetch doesn't happen onChange in my select only after changing option twice, I have tried to use useEffect but the problem with that it's fetching immediately without changing the select value.

can someone help and explain what I'm doing wrong?

here is my code on codesandbox

index.js

function App() {
  const [list,setList]=useState([])
  const [genre,setGenre]=useState(0)


  const fetchData=()=>{
    const options = {
      method: 'GET',
      url: `https://jikan1.p.rapidapi.com/genre/anime/${genre}/1`,
      headers: {
        'x-rapidapi-host': 'jikan1.p.rapidapi.com',
        'x-rapidapi-key': '*******************************'
      }
    };
    
    axios.request(options).then(function (response) {
      console.log(response.data.anime);
      setList(response.data.anime)
    }).catch(function (error) {
      console.error(error);
    });
  }

    

  const handleChange=(e)=>{
    setGenre(e.target.value)
    fetchData()
    
  }
  
  return (
    <div className="mx-auto px-8 py-4 flex flex-col justify-center">

      <div>
      <h1>Design with Tailwind</h1>
      
      <select name="genre" className='p-2 bg-bg-prime'  id="genre"  onChange={handleChange}>
            <option value="1" >Action</option>
            <option value="2" >Adventure</option>
            <option value="4" >Comedy</option>
      </select>
      </div>
        <div>
          {list && (
            list.map((an)=>(
              <p key={an.title}>{an.title}</p>
            ))
          )}
        </div>
    </div>
  )
}

const rootElement = document.getElementById('root')
ReactDOM.render(<App />, rootElement)

Dhaifallah
  • 1,437
  • 1
  • 5
  • 19
  • There's no reason for `fetchData` to be async. You're not `awaiting` anything. And your codesandbox example doesn't contain any of the code you've added here. – Andy Mar 15 '22 at 18:25
  • @Andy, thanks but that was not the problem, sorry about the link I didn't save it, now it's there, could you please have a look? – Dhaifallah Mar 15 '22 at 18:31
  • Whatever https://jikan1.p.rapidapi.com/ is it's not doing anything. Are you sure it's working? I'm getting `ERR_EMPTY_RESPONSE`. – Andy Mar 15 '22 at 18:40
  • @Andy yes it's working, maybe it has something to do with `API_KEY` , but I found a solution thanks for helping – Dhaifallah Mar 15 '22 at 18:50

2 Answers2

2

While you are calling fetchData after the setGenre, according to react docs :

State Updates May Be Asynchronous

So basically fetchData is called before the state value is actually updated (and this is causing your request to be performed with the old value instead of the requested one). A fix would be something like:

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

const handleChange=(e)=>{
  setGenre(e.target.value)
}

If you really want to keep your approach you could also do something like:

const handleChange=(e)=>{
  setGenre(e.target.value)
  fetchData(e.target.value)
}

and use the variable in fetch data (so

const fetchData = (selectedGenre) => {
  ...
    url: `https://jikan1.p.rapidapi.com/genre/anime/${selectedGenre}/1`,
  ...

I recommend first approach, but both should work.

Also if you use a valid value as the initial value for the genre along with the first approach you can get the list populated by default (for your example const [genre,setGenre]=useState(1) )

Berci
  • 2,876
  • 1
  • 18
  • 28
1

I couldn't see anything in your codesandbox.

I think the right approach would be to use the useEffect as you were trying, this way you make sure the 'genre' value you're using in your 'fetchData' function is the right one.

Now, the only thing that you were missing was an state to prevent the 'fetchData' function from running right after the component is rendered and make it run only after you've changed the 'genre' state for the first time.

function App() {
  const [list,setList]=useState([])
  const [genre,setGenre]=useState(0)
  const [selectCount, setSelectCount]=useState(0)

  const fetchData=async()=>{
    const options = {
      method: 'GET',
      url: `https://jikan1.p.rapidapi.com/genre/anime/${genre}/1`,
      headers: {
        'x-rapidapi-host': 'jikan1.p.rapidapi.com',
        'x-rapidapi-key': '*******************************'
      }
    };
    
    axios.request(options).then(function (response) {
      console.log(response.data.anime);
      setList(response.data.anime)
    }).catch(function (error) {
      console.error(error);
    });
  }

  const handleChange=(e)=>{
    setSelectCount(selectCount + 1)
    setGenre(e.target.value)
  }

  useEffect(() => {
    if (selectCount === 0) return
    fetchData()
  }, [genre])
  
  return ...Your JSX goes here...
}

const rootElement = document.getElementById('root')
ReactDOM.render(<App />, rootElement)