1

In my app I type in a string, which is then immediately invoked in rest api call and json object is returned containing all drinks according to the name typed in. Then I would like to display a specific part of that object, for example the name, but I get undefined error when accesssing that part of the object, which I will elaborate later. I am implementing react-navigation and react-elements and variables are refreshed by hooks.

export default class App extends Component {
    render() {    
        console.log('change')
        return (
            <NavigationContainer>
                <Drawer.Navigator initialRouteName="Home">
                    <Drawer.Screen name="Home" component={HomeScreen} />
                    <Drawer.Screen name="Notifications" component={NotificationsScreen} />
                </Drawer.Navigator>
            </NavigationContainer>   
        );
    }
}

The problem is in Home, my code works as far as displaying full object after each typed character: I can display object of all the drinks containing queried name that I typed in. But then, I want to display a specific element in the object, for example the first id (object.drinks[0].idDrink), which I assume doesnt load up yet and there comes the undefined error. I could access the id before when doing things without hooks, that part is not wrong. I assume I broke things implementing many hooks that are refreshing my app.

function HomeScreen() {
  
    const [query, setQuery] = useState('');
    const [jsonData, setJsonData] = useState([]);
    const [loading, setLoading] = useState(true);
    console.log(loading, '1') 
    useEffect(() => {
        setLoading(true);
        console.log(loading, '2') 

        fetch('https://www.thecocktaildb.com/api/json/v1/1/search.php?s=' + query, {
            method: 'GET',
        })
            .then(response => response.json())
            .then(jsonD => setJsonData(jsonD), 
                setLoading(false),
                console.log(loading, '3'))
            .catch(error => {
                console.error(error);
            });
    }, [query])  

    return (
        <ScrollView >
            <Text>Bartender</Text>
            <SearchBar
                placeholder="Type Here..."
                onChangeText={setQuery}
                value={query}
            />
           <>
                { 
                    loading ? console.log('waiting for loading') : console.log(jsonData.drinks[0].idDrink)
                }
           </>    
        </ScrollView >
    );
}

I tried to fix the problem by setting loading variable, which would render the object after it is fully loaded, but it does not fix it.

If I run the app displaying console log instead of specific element:

loading ? console.log('waiting for loading') : console.log('the data should be loaded'),

this is the output:

[Thu Dec 17 2020 20:20:07.902]  LOG      change
[Thu Dec 17 2020 20:20:08.159]  LOG      true 1
[Thu Dec 17 2020 20:20:08.175]  LOG      waiting for loading
[Thu Dec 17 2020 20:20:13.218]  LOG      true 2
[Thu Dec 17 2020 20:20:13.284]  LOG      true 3
[Thu Dec 17 2020 20:20:13.451]  LOG      false 1
[Thu Dec 17 2020 20:20:13.486]  LOG      the data should be loaded
[Thu Dec 17 2020 20:20:14.761]  LOG      false 1
[Thu Dec 17 2020 20:20:14.775]  LOG      the data should be loaded

after I type a character in the search bar, the output is:

[Thu Dec 17 2020 20:23:54.892]  LOG      false 1
[Thu Dec 17 2020 20:23:54.938]  LOG      the data should be loaded
[Thu Dec 17 2020 20:23:55.526]  LOG      false 2
[Thu Dec 17 2020 20:23:55.781]  LOG      false 3
[Thu Dec 17 2020 20:23:55.807]  LOG      false 1
[Thu Dec 17 2020 20:23:55.840]  LOG      the data should be loaded
[Thu Dec 17 2020 20:23:56.294]  LOG      false 1
[Thu Dec 17 2020 20:23:56.312]  LOG      the data should be loaded

TLDR: How do I make sure that I access the object refreshed by hooks after it is fully loaded? app

2
  • 2
    jsonD => setJsonData(jsonD), setLoading(false), console.log(loading, '3') isn't valid, there should be a function body. React state updates are also asynchronous, so you can't console.log(loading, '2') right after setLoading(true); and expect it to log the state just enqueued. Commented Dec 17, 2020 at 20:34
  • 1
    I think you should split out your UI... conditionally render a loading indicator based on the loading state, conditionally render jsonData when it is populated. Also, it may help to keep your state consistently typed, i.e. initial state of jsonData is an empty array but your UI code accesses as if it were an object. The array obviously won't ever have a drinks property to access an array index of. Commented Dec 17, 2020 at 20:46

1 Answer 1

1

I recommend you to go about the problem differently.

Instead of making the default value of jsonData an array you could set the default value to null:

function HomeScreen() {
  const [query, setQuery] = useState("");
  const [jsonData, setJsonData] = useState(null);

  useEffect(() => {
    fetch("https://www.thecocktaildb.com/api/json/v1/1/search.php?s=" + query, {
      method: "GET",
    })
      .then((response) => response.json())
      .then((jsonD) => setJsonData(jsonD))
      .catch((error) => {
        console.error(error);
      });
  }, [query]);

  return (
    // ...
       <>{jsonData && jsonData.drinks[0].idDrink}</>
    // ...
  );
}

This way you also don't need keep track of and update a loading state.

The problem with the other approach as you've experienced is that you can't be sure that the jsonData state has been updated before the loading state has.

Sign up to request clarification or add additional context in comments.

3 Comments

This solution works, but I have no idea what is the object and && operator for. And how come after I type in another string which calls fetch, which loads the variable from the start, it fully loads and I can access its parts immediately without getting undefined variable?
I think this is really easiest way to load things after they are truly loaded but I ve never seen similar solution before I searched for answers, thanks!
Using && in this way is called short circuit evaluation. This answer also explains it nicely if you want to know more stackoverflow.com/a/60652778/9098350.

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.