Reputation: 69
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'));
}
Upvotes: 4
Views: 23438
Reputation: 1
import { useState } from "react"
function App() {
const [data, setData] = useState(null)
const [error, setError] = useState(null)
const [loading, setLoading] = useState(false)
function fetchHanlde() {
const url = 'https://jsonplaceholder.typicode.com/posts'
fetch(url)
.then((response) => response.json())
.then((data) => {
setData(data);
setLoading(false)
})
.catch((error) => {
setError(error)
setLoading(false)
})
.finally(() => setLoading(false))
return { data, loading, error }
}
return <>
<h1>hook pesonnalisé </h1>
<button onClick={fetchHanlde}>get posts</button>
{loading && <div>Loading...</div>}
{data && <div>
<ul>
{data.map(post => (<li key={post.id}>{post.title}</li>))}
</ul>
</div>}
{error && <div>error : {error.message}</div>}
</>
}
export default App
Upvotes: 0
Reputation: 69
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>
);
}
Upvotes: 2
Reputation: 368
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
Upvotes: 1
Reputation: 4147
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.
Upvotes: 0