export default function Main({ match }) {
const userid = match.params.id;
const [user, setUser] = useState([]);
async function fetchuser() {
const response = await api.get('/emps/profile', {
headers: {
userid: match.params.id,
},
});
setUser(response.data);
console.log(response.data);
console.log(user);
}
useEffect(() => {
fetchuser();
}, [match.params.id]);
In the above code the response.data is written into console but user state is empty. Can someone tell me why this is?
Two suggestions:
Determine if response contains any data before logging it.
Move your fetchData function into the useEffect hook. https://stackoverflow.com/a/56851963/8943092
Below is an example of how you can test for the existence of data, and here is a live Sandbox.
Note that we use a simple conditional to check if (myData) is truthy. Our useState hook sets no default value, so the conditional returns true once data is present.
In the render method, we use a ternary to check for the existence of data.
Your solution may be slightly different because you set the default value of user to an empty array []. Assuming your API call returns an array, you'll test for data with if (user.length > 0).
import React, { useEffect, useState } from "react";
export default function App() {
const [myData, setMyData] = useState();
useEffect(() => {
function fetchData() {
setTimeout(function () {
setMyData("I am user data");
}, 3000);
}
if (myData) {
console.log(myData);
} else {
console.log("No data yet");
}
fetchData();
}, [myData]);
return (
<div className="App">{myData ? <p>{myData}</p> : <p>No data yet</p>}</div>
);
}
Related
For this piece of code:
import { useEffect, useState } from 'react';
import 'ag-grid-community/dist/styles/ag-grid.min.css';
import 'ag-grid-community/dist/styles/ag-theme-material.min.css';
import Grid from '../Common/Grid';
import axios from 'axios';
function GetJudete() {
axios.get("http://localhost:5266/api/judete").then((response) =>
{
let data = response.data;
return data;
}
).catch(err => {
console.log("Eroare la aducerea datelor.");
})
};
function Localitati()
{
let judete;
useEffect(() => {
judete = GetJudete();
}, []);
console.log(judete);
const [columnDefs] = useState([
{ field: 'nume', filter: 'agTextColumnFilter', editable: true },
{ field: 'judetID', filter: 'agTextColumnFilter', editable: true },
])
return (
<Grid
baseLink='http://localhost:5266/api/localitati'
columnDefs={columnDefs}
/>
);
}
export default Localitati;
my console.log() prints undefined. I expected it to be filled with values, because I'm using async code in GetJudete() by using Promises.
I see that it does not work this way though.
Why and how can I fix this?
Thanks.
There are two major reasons why you're seeing undefined:
Code in useEffect is run after render. That's why any update to judete would happen after you already logged it. (Once it's set correctly, you would need to trigger a rerender in order to see a second log line.)
Your GetJudete function doesn't return anything, that's why you still would be getting undefined. (Your promise does return something, but that's never used anywhere.)
Here's a way to work around both issues:
import { useEffect, useState } from 'react';
import 'ag-grid-community/dist/styles/ag-grid.min.css';
import 'ag-grid-community/dist/styles/ag-theme-material.min.css';
import Grid from '../Common/Grid';
import axios from 'axios';
async function GetJudete() {
try {
const { data } = await axios.get("http://localhost:5266/api/judete");
return data;
} catch (_) {
console.log("Eroare la aducerea datelor.");
}
};
function Localitati()
{
// Use a state instead of a local variable, so that React knows about updates
const [judete, setJudete] = useState();
useEffect(() => {
async function getJudeteAsync() {
const result = await GetJudete();
setJudete(result);
}
// Trigger the async function from your effect
getJudeteAsync();
}, []);
// The first render will yield "undefined";
// once the AJAX call is successfully done, the setJudete above will cause a re-render and you should see a second log line with an actual value
console.log(judete);
const [columnDefs] = useState([
{ field: 'nume', filter: 'agTextColumnFilter', editable: true },
{ field: 'judetID', filter: 'agTextColumnFilter', editable: true },
])
return (
<Grid
baseLink='http://localhost:5266/api/localitati'
columnDefs={columnDefs}
/>
);
}
export default Localitati;
try this
async function getJudate(){
await axios.get(...).then(...)
}
useEffect(() => {
let judate = getJudate()
console.log(judate)
}, [])
You need to console.log data after it's resolved.
In your current code you are printing judetes when component is created. To fix your issue you should call console.log in .then since it's resolved some time after component creation.
You have 2 problems !
data fetching :
GetJudete function must return a Promise
so try :
async function GetJudete() {
try {
const response = await axios.get("http://localhost:5266/api/judete");
let data = response.data;
return data;
} catch (err) {
console.error(err);
}
};
hooks data fetching:
useEffect callbacks are synchronous to prevent race conditions so we need to put async function inside the useEffect callback
and also you should use states to do anything with your data other than that react don't know when something has changed to re-render component
useEffect(() => {
const fetchData = async () => {
const data = await GetJudete();
console.log(data);
// setState is required here !
return data;
}
fetchData();
}, [])
i am fetching the users from dummyapi. I want to list each user in a component before that i am trying to console.log each user with forEach, but it throws that error: "TypeError: Cannot read property 'forEach' of null". I set the users value null at the beginning. After i fetch the data, i changed the users value. When i console.log it, users comes null at first then it shows the data. I guess first it goes into foreach loop when users are null so it throws error. I tried this, but didn't work.
{ users !== null && users.forEach(user => {
console.log(user);
})}
This is my code:
import { useEffect, useState } from "react";
import "../styles/App.css";
import Header from "../components/Header";
import User from "./User";
import axios from "axios";
function App() {
const BASE_URL = "https://dummyapi.io/data/api";
const [users, setUsers] = useState(null);
useEffect(() => {
async function fetchData() {
try {
const response = await axios.get(`${BASE_URL}/user?limit=15`, {
headers: { "app-id": APP_ID },
});
setUsers(response.data);
} catch (error) {
console.log(error);
}
}
fetchData();
}, []);
users.forEach((user) => {
console.log(user);
});
return (
<>
<Header />
<div className="filter">
<h3 className="filter__title">USER LIST</h3>
<div>
<input type="text" placeholder="Search by name" />
</div>
</div>
</>
);
}
export default App;
u can put question mark after user object like that so u check if user is aready
exist
users?.forEach((user) => {
console.log(user);
});
Run your forEach loop inside a useEffect hook:
useEffect(() => {
users && users.length && users.forEach((user) => {
console.log(user);
});
}, [users]);
This useEffect hook will only run when the value of users changes.
You can add a check first when you map the users like:
users && users.forEach((user) => {
console.log(user);
});
A a bit cleaner and more testable case:
Note: You can also wrap the function in useCallback function to avoid calling it when the URL doesn't change.
You need to initialize your user state like:
const [users, setUsers] = useState([]);
When checking for users you need to check if the length of your user array is good
users.length > 0 && users.forEach(user => {
// Your Logic
})
or use a isEmpty() from a lib like lodash or ramda
const fetchData = async (url) => {
const response = await axios.get(`${url}/user?limit=15`, {
headers: { "app-id": APP_ID }
});
const responseData = await response?.data?.data;
if (responseData) {
setUsers(responseData);
}
};
useEffect(() => {
fetchData(BASE_URL);
}, []);
Another approach you can use is set the initial value to be empty array [] instead of null.
const [users, setUsers] = useState([]);
that way you don't need null check even if you call array functions to it.
Context
All of my components need to fetch data.
How I fetch
Therefore I use a custom hook which fetches the data using the useEffect hook and axios. The hook returns data or if loading or on error false. The data is an object with mostly an array of objects.
How I render
I render my data conditional with an ternary (?) or the use of the short circuit (&&) operator.
Question
How can I destructure my data dependent if my useFetch hook is returning false or the data in a way i can reuse the logic or an minimal implementation to the receiving component?
What I have tried
moving the destructuring assignment into an if statement like in return. Issue: "undefined" errors => data was not available yet
moving attempt 1 to function. Issue: function does not return variables (return statement does not work either)
//issue
fuction Receiver() {
const query = headerQuery();
const data = useFetch(query);
const loaded = data.data //either ```false``` or object with ```data```
/*
The following part should be in an easy condition or passed to an combined logic but I just dont get it
destructuring assignment varies from component to component
ex:
const {
site,
data: {
subMenu: {
description,
article_galleries,
image: {
caption,
image: [{url}],
},
},
},
} = data;
*/
return loaded?(
<RichLink
title={title}
text={teaserForText}
link={link}
key={id}
></RichLink>):<Loading />
(
//for context
import axios from "axios";
import {
useHistory
} from "react-router-dom";
import {
useEffect,
useState
} from "react";
function useFetch(query) {
const [data, setData] = useState(false);
const [site, setSite] = useState(""); // = title
const history = useHistory();
useEffect(() => {
axios({
url: "http://localhost:1337/graphql",
method: "post",
data: {
query: query,
},
})
.then((res) => {
const result = res.data.data;
setData(result);
if (result === null) {
history.push("/Error404");
}
setSite(Object.keys(result)[0]);
})
.catch((error) => {
console.log(error, "error");
history.push("/Error");
});
}, [query, history, setData, setSite]);
return {
data: data,
site: site
};
}
export default useFetch;
)
You can return the error, data and your loading states from your hook. Then the component implementing the hooks can destructure all of these and do things depending upon the result. Example:
const useAsync = () => {
// I prefer status to be idle, pending, resolved and rejected.
// where pending status is loading.
const [status, setStatus] = useState('idle')
const [data, setData] = useState([])
const [error, setError] = useState(null)
useEffect(() => {
setStatus('pending')
axios.get('/').then(resp => {
setStatus('resolved')
setData(resp.data)
}).catch(err => {
setStatus('rejected') // you can handle error boundary
setError(err)
})
}, []}
return {status, data, error}
}
Component implementing this hook
const App = () => {
const {data, status, error} = useAsync()
if(status === 'idle'){
// do something
}else if(status === 'pending'){
return <Loader />
}else if(status === 'resolved'){
return <YourComponent data ={data} />
}else{
return <div role='alert'>something went wrong {error.message}</div>
}
}
the hooks can be enhanced more with the use of dynamic api functions.
Can someone please tell me the equivalent code using hooks for the following:
componentDidMount() {
const { match: { params } } = this.props;
axios.get(`/api/users/${params.userId}`)
.then(({ data: user }) => {
console.log('user', user);
this.setState({ user });
});
}
The exact functionality to match your class component into a functional component with hooks would be the following:
import * as React from "react";
import { useParams } from "react-router-dom";
const Component = () => {
const { userId } = useParams();
const [state, setState] = React.useState({ user: null });
React.useEffect(() => {
axios.get(`/api/users/${userId}`)
.then(({ data: user }) => {
console.log('user', user);
setState({ user });
});
}, []);
}
React.useEffect(() => {}, []) with an empty dependency array essentially works the same way as the componentDidMount lifecycle method.
The React.useState hook returns an array with the state and a method to update the state setState.
References:
https://reactjs.org/docs/hooks-state.html
https://reactjs.org/docs/hooks-effect.html
As an aside, and pointed out by #Yoshi:
The snippet provided is error prone, and the "moving to hooks" snippet will have the same errors that occur in the example. For example, as the request is in componentDidMount, if the userId changes it won't trigger a fetch to get the user data for the userId. To ensure this works in the hook, all you need to do is provide the userId in the dependency array in the useEffect...
const latestRequest = React.useRef(null);
React.useEffect(() => {
latestRequest.current = userId;
axios.get(`/api/users/${userId}`)
.then(({ data: user }) => {
if (latestRequest.current == userId) {
setState({ user });
}
});
}, [userId]);
I am getting information from 2 apis, one which is the Unsplash api which returns an random image and a 2nd api which returns a bible verse from ourmanna.com via the cors-anywhere proxy. The unsplash api works fine and always returns an image. The bible api returns the correct data but the useState does not update the state and verse stays as an empty string. I can't see why this is the case.
Below is the code:
import React, { useState, useEffect, Fragment } from "react";
import styles from "./BibleQuote.module.css";
import axios from "axios";
import Poster from "../Poster/Poster";
const UNSPLASH_API = process.env.REACT_APP_UNSPLASH_API;
const UNSPLASH_API_KEY = process.env.REACT_APP_UNSPLASH_API_KEY;
function BibleQuote() {
const [photo, setImage] = useState({ poster: "" });
const [verse, setVerse] = useState({ verse: "" });
useEffect(() => {
async function fetchData() {
await axios(UNSPLASH_API, {
headers: {
Authorization: `Client-ID ${UNSPLASH_API_KEY}`,
},
}).then((res) => {
setImage(res.data.urls.regular);
console.log("image", res.data.urls.regular);
});
await axios(
`https://cors-anywhere.herokuapp.com/http://www.ourmanna.com/verses/api/get?format=text&order=random`).then((res) => {
setVerse(res.data);
console.log("APIquote", res.data);
console.log("State: Quote", verse);
});
}
fetchData();
}, []);
return (
<Fragment>
<Poster photo={photo} quote={verse} />
</Fragment>
);
}
export default BibleQuote;
this is the result in the chrome developer tools:
I am pretty sure it is updated but you can not see it from console.log right after the state is changed. Try to use another one useEffect working only when state is changed.
const App = () => {
const [data, setData] = useState(false)
useEffect(()=> {
setData(true)
console.log(data) //shows false despite of setData on the previous line
}, [])
useEffect(()=> {
console.log(data) //shows true - updated state
}, [data])
return <h2>Hello world</h2>
}
That's because setState works async so the next line always contain the previous value of the state.