setState in useEffect depanding on props.location.state, doesn't work - reactjs

I do not understand what I am missing here, I would be very grateful for your help!
I'm getting to my "Test" functional component, from another functional component, by the command history.push (urlState).
"urlState" is an object, which I check its values in the useEffect function of the "Test" component.
Based on the values in urlState, I decide whether to execute "setRequestNumber" and "setRequestSubNumber", and which values to send to them.
But the set functions do not work !!
What am I doing wrong ??
(I want useEffect to run only once, so I left the dependencies array-blank.)
Here is my code-
import React, { useState, useEffect } from "react";
import {URL} from "../../config/Constants"
const Test = (props) => {
const [requestNumber, setRequestNumber] = useState(0);
const [requestSubNumber, setRequestSubNumber] = useState(1);
const getRequestDetails = async (fetchURL) => {
try {
const response = await fetch(fetchURL);
if (!response.ok) {
throw new Error("something went wrong!");
}
const body = await response.json();
const result = body.results;
console.log(result); //result will be rendered instead of <h1>Hi there!</h1>
} catch (error) {}
};
const getNextRequestCounter = async () => {
try {
const response = await fetch(`${URL}/requests/requestCounter`);
if (!response.ok) {
throw new Error("something went wrong!");
}
const body = await response.json();
const result = body.results[0];
setRequestNumber(result);
} catch (error) {
}
};
useEffect(() => {
var urlState = {
request: 1,
subRequest: 2,
isNewRequest: false,
originalSubRequest: 1,
}; // That what I have in props.location.state;
var fetchURL = "";
if (urlState.isNewRequest) {
getNextRequestCounter();
fetchURL = `${URL}/requests/${requestNumber}/${requestSubNumber}`;
} else {
setRequestNumber(urlState.request);
setRequestSubNumber(urlState.subRequest);
fetchURL = `${URL}/requests/${requestNumber}/${urlState.originalSubRequest}`;
}
getRequestDetails(fetchURL);
}, []);
return <h1>Hi there!</h1>;
};
export default Test;
Thank you!

Related

React - useEffect based on another useEffect

I'm trying to understand how useEffect works.
I have two callApi: "callApiDialer" is based on response of "callApiManager", for get id from list.
But "currentLeadId" state at first called obviously is null.
How can call "callApiDialer" when currentLeadId is not null?
import React, { useState, useEffect } from 'react';
const [loading, setLoading] = useState(true);
const [apiManager, setApiManager] = useState([]);
const [apiDialer, setApiDialer] = useState([]);
const [currentLeadId, setCurrentLeadId] = useState(null);
// CALL API
const callApiManager = async () => {
try {
const response = await api.get(`/api/manager/op/1`);
setCurrentLeadId(response.data.dialer_list[0].id);
setApiManager(response.data);
} catch (err) {
alert("fetchApiManager " + err.response.status);
}
}
const callApiDialer = async () => {
try {
const response = await api.get(`/api/manager/lead/${currentLeadId}`);
setApiDialer(response.data.lead);
setLoadingModal(false);
} catch (err) {
alert("fetchApiSources " + err.response.status);
}
}
useEffect(() => {
callApiManager();
}, [])
useEffect(() => {
console.log(currentLeadId); // <-- here get first null and after currentLeadId
if(currentLeadId) {
callApiDialer();
setLoading(false);
}
}, [currentLeadId])
You could have just one function that call both, therefore there would be only one useEffect.
// CALL API
const callBothApisAtOnce= async () => {
try {
const op = await api.get(`/api/manager/op/1`);
const response = await api.get(`/api/manager/lead/${op.data.dialer_list[0].id}`);
// rest of your logic...
} catch (err) {
alert("err" + err);
}
}
useEffect(() => {
callBothApisAtOnce()
}, [])
you can use axios's promise base functionality
axios.get(`/api/manager/op/1`).then(res => {
setCurrentLeadId(response.data.dialer_list[0].id);
setApiManager(response.data);
axios.get(`/api/manager/lead/${response.data.dialer_list[0].id}`).then(res1 =>{
setApiDialer(res1.data.lead);
setLoadingModal(false);
}
}

UseEffect mutiple re-renders after async api call and make changes in UI after 1 sec of first call

I'm making a Quiz app, using API from (Trivia api),
Issues is - As soon as the api call is made the state is changes 3 times and my UI data changes 2 times in 1 second.
I think the issue is related to useEffect even though i'm adding empty dependency in useEffect.
can anybody explain why is it happening?
Layout.js
import { useEffect, useState } from 'react'
import { Outlet } from 'react-router-dom'
import Header from '../Componenets/Header/Header'
import ProgressBar from '../Componenets/ProgressBar/ProgressBar'
import QuizMain from '../Componenets/QuizMain/QuizMain'
function Layout() {
const [questionAre, setQuestionsAre] = useState([])
const [currentQuestion, setCurrentQuestion] = useState(0)
const changeCurrentQuestion = (value) => {
setCurrentQuestion(value)
}
useEffect(() => {
const QuizFetch = async () => {
try {
const res = await fetch(
'https://the-trivia-api.com/api/questions?categories=food_and_drink,general_knowledge&limit=10&region=AU&difficulty=easy',
)
const data = await res.json()
const transformData = data.map((item) => {
const newarray = item.incorrectAnswers
return {
options: newarray,
question: item.question,
correctAnswer: item.correctAnswer,
}
})
setQuestionsAre(transformData)
} catch (err) {
console.log(err, 'err in getting data')
}
}
QuizFetch()
}, [])
return (
<div className="Layout">
<Header />
<ProgressBar
changeCurrentQuestion={changeCurrentQuestion}
currentQuestion={currentQuestion}
questionAre={questionAre}
/>
{/* <QuizMain
changeCurrentQuestion={changeCurrentQuestion}
currentQuestion={currentQuestion}
questionAre={questionAre} /> */}
<Outlet context={[changeCurrentQuestion, currentQuestion, questionAre]} />
</div>
)
}
export default Layout
Since react 18 and the lifecycle in dev mode you have to use the abortController.
The signal will jump to the catch and then you will only have one successfull api call
useEffect(() => {
const abortController = new AbortController();
const QuizFetch = async () => {
try {
const res = await fetch(
'https://the-trivia-api.com/api/questions?categories=food_and_drink,general_knowledge&limit=10&region=AU&difficulty=easy',
{
signal: abortController.signal,
},
)
const data = await res.json()
const transformData = data.map((item) => {
const newarray = item.incorrectAnswers
return {
options: newarray,
question: item.question,
correctAnswer: item.correctAnswer,
}
})
setQuestionsAre(transformData)
} catch (err) {
if (abortController.signal.aborted) return;
console.log(err, 'err in getting data')
}
}
QuizFetch()
return () => {
abortController.abort();
};
}, [])

React Native AsyncStorage getAllKeys infinite loop

This AsyncStorageHooks react custom hook
import {useState} from 'react';
import AsyncStorage from '#react-native-async-storage/async-storage';
const AsyncStorageHooks = (key, value, mergedValue, keys) => {
const [data, setData] = useState('');
const [error, setError] = useState('');
const storeData = async () => {
try {
if (key && value) {
const jsonValue = JSON.stringify(value);
await AsyncStorage.setItem(key, jsonValue);
setData(jsonValue);
}
} catch (e) {
setError(e);
}
};
const getData = async () => {
try {
if (key) {
const jsonValue = await AsyncStorage.getItem(key);
setData(jsonValue != null ? JSON.parse(jsonValue) : '');
}
} catch (e) {
setError(e);
}
};
const mergeData = async () => {
try {
if (key && value && mergedValue) {
const jsonValue = JSON.stringify(value);
const mergedJsonValue = JSON.stringify(mergedValue);
await AsyncStorage.setItem(key, jsonValue);
await AsyncStorage.mergeItem(key, mergedJsonValue);
}
} catch (e) {
setError(e);
}
};
const removeData = async () => {
try {
if (key) {
await AsyncStorage.removeItem(key);
}
} catch (e) {
setError(e);
}
};
const getAllKeys = async () => {
let allKeys = [];
try {
allKeys = await AsyncStorage.getAllKeys();
setData(allKeys);
} catch (e) {
setError(e);
}
};
return {
data,
storeData,
getData,
removeData,
mergeData,
getAllKeys,
error,
};
};
export default AsyncStorageHooks;
this is my home component
const {data, error, getData, storeData, getAllKeys} =
useAsyncStorage('#word');
getData(); // this is works and use setData
storeData(); // this is works and use setData
getAllKeys();
console.log(data);
this works without any problems, they use the same page in the same state. It doesn't go into an infinite loop. The only getAllKeys dosen't works. Other functions works without any problems.
also side note: setData(allKeys); change to setData(allKeys + ''); or setData(JSON.stringify(allKeys)); stop to infinity loop why is that
You are getting an infinite loop because getAllKeys() is being called again and again. The first time it is called, there is this setData(keys) being called, which re-renders the component, because there is a state change.
When the component re-renders, getAllKeys() is called again, so setData(keys) is called, and it goes for ever. You would wanna use a useEffect to solve this problem, like so:
import {Text, View} from 'react-native';
import React, {useState, useEffect} from 'react';
import AsyncStorage from '#react-native-async-storage/async-storage';
const Home = () => {
const [data, setData] = useState([]);
useEffect(()=>{
const getAllKeys = async () => {
let keys = [];
try {
keys = await AsyncStorage.getAllKeys();
setData(keys);
} catch (e) {
// read key error
}
};
getAllKeys();
},[])
console.log(data);
return (
<View>
<Text>hi</Text>
</View>
);
};
export default Home;

Combine two Axios calls inside one useEffect with async/await

I'm currently trying to understand how to work with async/await in React JS. In this demo app, I'm trying to get full border names of the chosen country by calling https://restcountries.eu/. I make first API call to get info about country and the second one to get full name of its borders since first API call returns
only short border names. I believe there is a way to combine those calls inside one useEffect however everything I tried gave me some sort of an error or getting stuck in infinite loop. What is the proper way to combine those calls with async/await approach?
import React, { useState, useEffect } from "react";
import Axios from "axios";
const App = () => {
const [loading, setLoading] = useState(true);
const [country, setCountry] = useState({});
const [fullBorderNames, setFullBorderNames] = useState([]);
//FIRST API CALL
useEffect(() => {
const source = Axios.CancelToken.source();
const fetchData = async () => {
setLoading(true);
try {
const response = await Axios(
`https://restcountries.eu/rest/v2/name/canada?fullText=true`,
{ cancelToken: source.token }
);
setCountry(response.data[0]);
} catch (err) {
if (Axios.isCancel(err)) {
console.log("Axios canceled");
} else {
console.log(err);
}
}
};
fetchData();
return () => source.cancel();
}, []);
//SECOND API CALL
useEffect(() => {
const source = Axios.CancelToken.source();
let borders = [];
if (country.borders) {
const fetchData = async () => {
try {
country.borders.forEach(async border => {
const response = await Axios(
`https://restcountries.eu/rest/v2/alpha?codes=${border}`,
{ cancelToken: source.token }
);
borders.push(response.data[0].name);
if (borders.length === country.borders.length)
setFullBorderNames(borders);
});
} catch (err) {
if (Axios.isCancel(err)) {
console.log("Axios canceled");
} else {
console.log(err);
}
}
setLoading(false);
};
fetchData();
}
return () => source.cancel();
}, [country.borders]);
if (loading) {
return <h2>Loading</h2>;
} else {
return (
<>
<pre>{JSON.stringify(country, null, 2)}</pre>
<pre>{JSON.stringify(fullBorderNames, null, 2)}</pre>
</>
);
}
};
export default App;
You can simply just make the requests right after the first one.
try {
const response = await Axios(`https://restcountries.eu/rest/v2/name/canada?
fullText=true`, { cancelToken: source.token });
const country = response.data[0];
setCountry(country);
/* all the other fetch calls*/
Can you tell me what kind of errors you get because I don't see an issue with doing them in the same useEffect? It just gets a little messy which can be refactored anyway.

Re-calling useEffect after a failed api fetching request

I am executing useEffect() to update a state with JSON data. However the fetch request sometimes fails, so I want to re-execute the useEffect hook if that happens:
...
import React, {useState, useEffect} from 'react';
import {getJsonData} from './getJsonData';
const myApp = () => {
var ErrorFetchedChecker = false;
const [isLoading,setIsLoading] = useState(true);
const [data,setData] = useState(null);
const updateState = jsonData => {
setIsloading(false);
setData(jsonData);
};
useEffect(() => {
//console.log('EXECUTING');
getJsonData().then(
data => updateState(data),
error => {
Alert.alert('DATA FETCHING ERROR !', 'Refreshing ?...');
ErrorFetchedChecker = !ErrorFetchedChecker;
//console.log('LOG__FROM_CountriesTable: Executed');
},
);
}, [ErrorFetchedChecker]);//Shouldn't the change on this variable
//be enough to re-execute the hook ?
return (
<View>
<Text>{state.data.title}</Text>
<Text>{data.data.completed}</Text>
</View>
);
}
Here's the getJsonData() function just in case:
export async function getJsonData() {
try {
let response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
let responseJson = await response.json();
return responseJson;
} catch (error) {
throw error;
// Also, is this the correct way to handle the error ?
// As the Alert in useEffect goes off either ways.
// If not, advise me on how the error should be handled.
}
}
This will work
const myApp = () => {
const [errorFetchedChecker, setErrorFetchedChecker] = useState(false);
const [isLoading,setIsLoading] = useState(true);
const [data,setData] = useState(null);
const updateState = jsonData => {
setIsloading(false);
setData(jsonData);
};
useEffect(() => {
//console.log('EXECUTING');
getJsonData().then(
data => updateState(data),
error => {
Alert.alert('DATA FETCHING ERROR !', 'Refreshing ?...');
setErrorFetchedChecker(c => !c);
//console.log('LOG__FROM_CountriesTable: Executed');
},
);
}, [errorFetchedChecker]);
return (
<View>
<Text>{state.data.title}</Text>
<Text>{data.data.completed}</Text>
</View>
);
}
import React, { useState, useRef, useEffect } from "react";
import { Text, View, TextInput } from "react-native";
const App = () => {
var ErrorFetchedChecker = false;
const [isLoading, setIsLoading] = useState(true);
const [data, setData] = useState(null);
const updateState = (jsonData) => {
setIsLoading(false);
setData(jsonData);
};
useEffect(() => {
//console.log('EXECUTING');
getJsonData()
.then((data) => {
console.log("1. Successful, just received the data from our promise");
updateState(data);
console.log("2. We set our data because we received it successfully");
return { alreadySet: true };
})
.catch((e) => {
console.log("1. We failed to gather data in our initial promise");
console.log("2. Attempting to rerun initial promise");
return getJsonData();
})
.then((data) => {
if (data.alreadySet) {
console.log(
"3. Did not attempt to retry because we are already successful"
);
} else {
console.log("3. Our second attempt succeeded");
updateState(data);
console.log("4. Set our data on our second attempt");
}
})
.catch((e) => {
console.log("3. Both attempts have failed");
});
}, []); //Shouldn't the change on this variable
//be enough to re-execute the hook ?
return (
<View>
<Text>{data ? <Text>{data.title}</Text> : null}</Text>
</View>
);
};
export async function getJsonData() {
try {
let response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
let responseJson = await response.json();
return responseJson;
} catch (error) {
throw error;
// Also, is this the correct way to handle the error ?
// As the Alert in useEffect goes off either ways.
// If not, advise me on how the error should be handled.
}
}
export default App;

Resources