Allyn Alda
Allyn Alda

Reputation: 63

Loading state does not change to true in fetch method

I've been trying to implement a loading functionality to my app. I'm simply changing the state of loading from false as the initial value and then true as it starts the fetch and then false as it ends the data fetching. So this should show the loading element I've set conditionally to render when loading is true. But it shows in my console.log that the value is always false.

I've tried putting the setState(true) in different places, in the onClick function but it doesn't seem to toggle to true.


import React, { useState } from "react";
import { LANGUAGES } from '../config/languages'
import { BASEURL, APIKEY } from '../config/gavagai'

export function Input(props) {

    const [word, setWord] = useState("");
    const [language, setLanguage] = useState("");
    const [data, setData] = useState([])
    const [loading, setLoading] = useState(false);
    const url = BASEURL + '/' + language + '/' + word + '?additionalFields=SEMANTICALLY_SIMILAR_WORDS&apiKey=' + APIKEY;


    const fetchData = () => {
        giveWarning();
        setLoading(true);
        if (word && language) {

            fetch(url)
            .then(response => response.json())   
            .then(response => setData({ status: 'loaded', payload: response }), setLoading(false))
            .catch(error => setData({ status: 'error', error }))
            return data;
      };
    }

  return (
      <div>
      <h1>Gavagai Lexicon</h1>
      <div className="row">
      <label>
        Type your word here
      </label>
      </div>
      <div className="input-field col s5">
        <input
          type="text"
          value={word}
          onChange={e => setWord(e.target.value)}
        />
        </div>
        <div className="input-field col s3">
          <select className="browser-default" value={language} onChange={e => setLanguage(e.target.value)}>
              <option value="" disabled selected>Choose your language</option>
              { LANGUAGES.map((lang) => {
                  return(
                    <option value={lang.alpha2}>{lang.English}</option>
                  )
              })}
          </select>
          </div>
      <div className="button-space">
      <button className="btn waves-effect waves-light" onClick={() => fetchData()}>Search</button>
      </div>
      {
        loading ? <p>loading</p> : null
      }
      </div>

  );
}

Console.log reveals that it doesn't toggle to true. What am I missing here?

Upvotes: 2

Views: 972

Answers (1)

Michal
Michal

Reputation: 469

Because of closure, fetchData has only access to the initial value of word and language variables.

You need to useCallback( your function, [word, language] ) to use the current values of those.

https://reactjs.org/docs/hooks-reference.html#usecallback

export function Input(props) { ...

const fetchData = useCallback(
    () => {
        giveWarning();
        setLoading(true);
        if (word && language) {

            fetch(url)
            .then(response => response.json())   
            .then(response => setData({
                status: 'loaded',
                payload: response
            }), setLoading(false))
            .catch(error => setData({ status: 'error', error }))

            return data;
        };
    },
    [word, language]
)

...

Upvotes: 0

Related Questions