4

I'm trying to fetch data in react. The problem is i have to click on button twice to get that data. Although i don't get data on first click it somehow renders if I add JSON.stringify to it. If I don't add JSON.stringify it returns undefined. If anyone know what this is please help me

without clicking
on first click
on second click

import React, {useState,useEffect} from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios'

function Example() {

const [students,setStudents] = useState('')
const [name,setName] = useState('')

const handleClick = async() => {
    const data = await axios.get('api/foo')
    setStudents(data)
    console.log(students)
}

return (
    <div className="container">
        <h2>Example component</h2>
        <button onClick = {handleClick}>Get students</button>
        <div>
            {JSON.stringify(students.data)}
        </div>
    </div>
);
 }

export default Example;

if (document.getElementById('root')) {
     ReactDOM.render(<Example />, document.getElementById('root'));
 }

3 Answers3

2

The problem was that setStudents is an asynchronous function, so I just made student object and added to it loading property

const [students,setStudents] = useState({
    data: '',
    loading: true
})
const [name,setName] = useState('')

const handleClick = async() => {
    const data = await axios.get('api/foo')
    setStudents({
        data: data,
        loading: false
    })
}

return (
    <div className="container">
        <h2>Example component</h2>
        <button onClick = {handleClick}>Get students</button>
        <div>
            {students.loading?'':
            students.data.data[0].name}
        </div>
    </div>
);

}

1

setStudent is an asynchronous function. This means the value of students won't change immediately after you call setStudents.

Try shifting the console.log outside the handleClick function. Like this -

import React, {useState,useEffect} from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios'

function Example() {

const [students,setStudents] = useState('')
const [name,setName] = useState('')

const handleClick = async() => {
    const data = await axios.get('api/foo')
    setStudents(data)
}

console.log(students)

return (
    <div className="container">
        <h2>Example component</h2>
        <button onClick = {handleClick}>Get students</button>
        <div>
            {JSON.stringify(students.data)}
        </div>
    </div>
);
 }

export default Example;

if (document.getElementById('root')) {
     ReactDOM.render(<Example />, document.getElementById('root'));
 }

Initially, the value will be an empty string, then it will change to the value from api/foo

Rudraprasad Das
  • 348
  • 1
  • 10
  • Thank you, but the problem is when i try to render `students.data` in jsx without `JSON.stringify` it returns error that `students.data` is undefined. Do I need to use UseEffect hook ? – Artsiom Aliaksandrau Nov 02 '20 at 15:25
0

React hooks are async so when you are running console.log(students) right after running setStudents(data) it is still not populated, however the 2nd time you click the button it is already populated from the first time you clicked it.

If you want to console the result right after the state setter runs you can see this answer on another question.

Shmili Breuer
  • 3,927
  • 2
  • 17
  • 26