useEffect: How to put data in the state in order - reactjs

I'd like to ask how to retrieve data through use Effect.
The flow I want is as follows.
First, I want to get the 'cards' state, fill the cards with data, and then fill the data through the cardsPromises after that.
But my code couldn't get cards and wordAll, and the empty value came out.
I think it's because the cards are still empty, but I don't know how to operate in order.
Please tell me how to do it.
const [wordAll, setWordAll] = useState([]);
const [cards, setCards] = useState([]);
useEffect(() => {
axios
.get("http/api/words/", {
headers: {
Authorization: cookies.token,
},
})
.then((response) => {
setCards(response.data);
})
.catch((error) => {
console.log(error);
});
const cardsPromises = cards.map((contents) =>
axios.get(
`http/api/words/detail_list/?contents=${contents.contents}`,
{
headers: {
Authorization: cookies.token,
},
}
)
);
console.log("cards", cards);
Promise.all(cardsPromises)
.then((response) => {
console.log("resp", response.data);
setWordAll(response.data);
})
.catch((error) => {
console.log("err==>", error);
});
}, []);

You are correct, cards array is still empty in the useEffect callback when the fetching the data. I suggest converting to async/await and waiting for the first fetch to resolve and using that value of cards for the fetching of the rest of the data.
const [wordAll, setWordAll] = useState([]);
const [cards, setCards] = useState([]);
useEffect(() => {
const fetchData = async () => {
try {
const{ data: cards } = await axios.get(
"http/api/words/",
{
headers: {
Authorization: cookies.token,
},
},
);
setCards(cards);
const cardsPromises = cards.map((contents) =>
axios.get(
`http/api/words/detail_list/?contents=${contents.contents}`,
{
headers: {
Authorization: cookies.token,
},
}
);
);
const wordAllResponse = await Promise.all(cardsPromises);
const wordAll = wordAllResponse.map(({ data }) => data);
setWordAll(wordAll);
} catch (error) {
// handle any errors, rejected Promises, etc..
}
};
fetchData();
}, []);

Wrap your 2nd axios call inside a function, and call it after 1st axios call returns.
useEffect(() => {
const getWords = (cards) => {
const cardsPromises = cards.map((contents) =>
axios.get(
`http/api/words/detail_list/?contents=${contents.contents}`,
{
headers: {Authorization: cookies.token}
}
)
);
Promise.all(cardsPromises)
.then((response) => {
setWordAll(response.data);
})
.catch((error) => {
console.log("err==>", error);
});
})
axios
.get("http/api/words/", {
headers: { Authorization: cookies.token },
})
.then((response) => {
const cards = response.data;
setCards(cards);
getWords(cards);
})
.catch((error) => {
console.log(error);
});
}, [])
Now dependency chain is clearer.

Related

dataLoaded state only changed after page reloaded

I'm doing an API call to get some data. then I keep a useState called dataLoaded. on a successful API call I make the dataLoaded=true. but to see it changed I have to reload the page.
following is my code.
const [dataLoaded, setDataLoaded] = useState(false)
useEffect(() =>{
const url = `${process.env.REACT_APP_DEV_BASE_URL}/v1/movie/`+ path.eventId + `/venue/`+ path.venue +`/showtime`;
const requestOptions = (token) => {
return ({
method: 'GET',
headers: { 'Content-Type': 'application/json', 'client_token': '4ece-9e89-1b6d4d2cbb61' }
})
};
const fetchData = async () => {
try {
const response = await fetch(url, requestOptions());
const json = await response.json();
// console.log(json);
// console.log(json.data.venueDateShowtime)
setShowTimes(json.data.dateShowtimes[0].showtimes[0]);
console.log(json.data.dateShowtimes[0].date)
setShowdate(json.data.dateShowtimes[0].date);
setDataLoaded(true);
console.log(dataLoaded)
console.log(showTimes.showtimeId)
console.log(showdate)
if(dataLoaded){
getSeatsArrangement();
}
console.log('jjjj')
}
catch (error) {
console.log("error",error);
}
};
fetchData();
},[]);
const getSeatsArrangement = async () => {
const requestOptions = (token) => {
return ({
method: 'GET',
headers: { 'Content-Type': 'application/json', 'client_token': '4ece-9e89-1b6d4d2cbb61' }
})
};
console.log(showTimes.showtimeId)
console.log(showdate)
try{
const url = `${process.env.REACT_APP_DEV_BASE_URL}/v1/seat?venueId=` + path.venue + `&movieId=`+ path.eventId +`&showtimeId=1011&movieDate=2022-10-11`;
const response = await fetch(url,requestOptions());
const json = await response.json();
console.log(json)
setReservedSeats(json.data.reservedSeats.reservedSeat)
setNonReservedSeats(json.data.reservedSeats.nonReservedSeats)
console.log(reservedSeats)
console.log(nonReservedSeats)
} catch(error) {
console.log("error",error);
}
}
Console logs when page loads
What is the aim of the code? fetchData is performed once after page loading (because of using ,[] at the end of useeffect.
And a remark: If you log your state right after setting it, the previous value will be shown! you should define another useeffect with your state as dependency (for each state) and log your state in there.
useEffect(() => {
console.log(dataLoaded)
if(dataLoaded){
getSeatsArrangement();
}
console.log('jjjj')
}, [dataLoaded]);
useEffect(() => {
console.log(showTimes.showtimeId)
}, [showTimes]);
useEffect(() => {
console.log(showdate)
}, [showdate]);
useEffect(() =>{
const url = `${process.env.REACT_APP_DEV_BASE_URL}/v1/movie/`+ path.eventId + `/venue/`+ path.venue +`/showtime`;
const requestOptions = (token) => {
return ({
method: 'GET',
headers: { 'Content-Type': 'application/json', 'client_token': '4ece-9e89-1b6d4d2cbb61' }
})
};
const fetchData = async () => {
try {
const response = await fetch(url, requestOptions());
const json = await response.json();
// console.log(json);
// console.log(json.data.venueDateShowtime)
setShowTimes(json.data.dateShowtimes[0].showtimes[0]);
console.log(json.data.dateShowtimes[0].date)
setShowdate(json.data.dateShowtimes[0].date);
setDataLoaded(true);
}
catch (error) {
console.log("error",error);
}
};
fetchData();
},[]);

React renders component many times when fetching data

I'm using fetch api so my goal is to fire a POST request and then store the result received from this call in a state. This is the code that I use:
interface IPreviewFile {
file: IFile;
}
export default function PreviewFileModal({ file }: IPreviewFile) {
const source = useSelector((state: AppState) => state.source);
const project = useSelector((state: AppState) => state.project);
const data = {
fileName: file.path,
accountName: source.Name,
bucket: source.bucket,
id: source.id
};
useEffect(() => {
Promise.all([
fetch(`${API_URL}/api/project/${project.id}/filepreview`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
])
.then(async ([aa]) => {
const a = await aa.json();
return [a];
})
.then((responseText) => {
setStringArray(readString(responseText[0].value).data);
})
.catch((err) => {
console.log(err);
});
}, [project.id, data]);
console.log(stringArray);
return (
<>
<div></div>
</>
);
}
The console.log(stringArray); prints in the console all the time after delay of 2-3 seconds. As you can see, I use Promise in order to avoid this but for some reason it still happens. Any ideas what causes the re-rendering all the time and how to fix it?
I've tried changing the code a bit to avoid re-rendering the component due to the data variable added as a dependency to useEffect. I don't see any reference to stringArray, so i've added it as a state variable.
export default function PreviewFileModal({ file }: IPreviewFile) {
const source = useSelector((state: AppState) => state.source);
const project = useSelector((state: AppState) => state.project);
const [stringArray, setStringArray] = useState("");
useEffect(() => {
fetch(`${API_URL}/api/project/${project.id}/filepreview`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
fileName: file.path,
accountName: source.Name,
bucket: source.bucket,
id: source.id
})
})
.then(res => res.json())
.then((result) => {
result && setStringArray(readString(result.value).data);
})
.catch((err) => {
console.log(err);
});
}, [project.id]);
console.log(stringArray);
return (
<>
<div></div>
</>
);
}

how to use aync and await inside use effect of calling function?

I am calling following functions like below.
How I can give async await to the serviceRequest and success handler
useEffect(() => {
serviceRequest(
"URL",
success,
error
);
}, []);
const success = (response) => { }
const error = (error) => { }
export const serviceRequest = (endpoint,successCallBack,errorCallBack) => {
const options: any = {
withCredentials: true,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
}
axios.get(endpoint, options)
.then((response) => {
successCallBack(response.data)
})
.catch(error => {errorCallBack(error) })
}
Well, first make sure that the serviceRequest function returns a promise. In this case you could simply return the axios result:
export const serviceRequest = (endpoint,successCallBack,errorCallBack) => {
const options: any = {
withCredentials: true,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
}
return axios.get(endpoint, options)
}
Then you can use the promise in your use-effect handler, catching the result / error in a useState hook like this:
const [result, setResult] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
serviceRequest("URL")
.then((result) => setState(result)
.catch(error => setState(error)
};
}, []);
You can decalre an async function in the useEffect and call it:
useEffect(() => {
const callApi = async () => {
await serviceRequest("URL", success, error);
};
callApi();
}, []);
You could write something like:
useEffect(() => {
(async () => {
await serviceRequest("URL", success, error);
})()
}, []);

How to set fetch data to text field in react-native function component

I am learning react-native and have a question about fetching data and passing them to a text component.
I fetched my data from my node.js back-end but don't know how to pass this data to component. Below is the code that i have tried so far.
const TableView = () => {
const [details, setDetails] = useState('');
const getUserData = async () => {
fetch('https://----.herokuapp.com/getIncomePerUser', {
method: 'post',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
email: data,
month: value,
}),
})
.then(response => response.json())
.then(response => {
console.log('Test');
console.log(response);
const array = response;
for (const i of array) {
const total = i.total;
setDetails(total);
console.log(total);
}
})
.catch(err => {
console.log(err);
});
});
};
useEffect(() => {
getUserData();
}, []);
return (
<Text Value={details}></Text> //I need to set my fetch data this text component
)
}
if you have an array of values and you want to show them you can use:
const TableView = () => {
const [details, setDetails] = useState('');
const getUserData = async () => {
fetch('https://----.herokuapp.com/getIncomePerUser', {
method: 'post',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
email: data,
month: value,
}),
})
.then(response => response.json())
.then(response => {
setDetails(response.map(r => r.total));
})
.catch(err => {
console.log(err);
});
});
};
useEffect(() => {
getUserData();
}, []);
return (<>
{details.map((d, i) => <Text key={i}>{d}</Text>)}
</>)
}
if you have a single value just replace your text component with:
<Text>{details}</Text>

Values won't update after api call with axios

I'm trying to fetch data with axios using React's useEffect in two steps:
1- Get the access token with a POST request
2- Use the token on another POST request to get the desired data
After the first post request, the response returns as expected, but the state value doesn't update. So it sends undefined as the token for the second request.
const [infos, setInfos] = useState(null)
const [token, setToken] = useState('')
useEffect(() => {
const getToken = async () => {
try {
const response = await axios.post(
'adress',
{
username: 'root',
password: 'i_want_my_token',
}
)
setToken(response.data.access)
} catch (error) {
console.log(error)
}
}
getToken()
const getCatalogo = async () => {
try {
let data = { id: 6 }
let configCatalogo = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
}
const catalogoResponse = await axios
.post(
'adress',
data,
configCatalogo
)
setInfos(res.data)
} catch (error) {
console.log(error) }
}
getCatalogo()
}, [])
Since the function to get/set your token is asynchronous, you need to use two different useEffects: one to fetch the token and one to use that token once it is set.
const [infos, setInfos] = useState(null)
const [token, setToken] = useState('')
useEffect(() => {
const getToken = async () => {
try {
const response = await axios.post(
'adress',
{
username: 'root',
password: 'i_want_my_token',
}
)
setToken(response.data.access)
} catch (error) {
console.log(error)
}
}
getToken()
}, []);
useEffect(() => {
const getCatalogo = async () => {
try {
let data = { id: 6 }
let configCatalogo = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
}
const catalogoResponse = await axios
.post(
'adress',
data,
configCatalogo
)
setInfos(res.data)
} catch (error) {
console.log(error) }
}
getCatalogo()
}, [token]);

Resources