useEffect didnt run - reactjs

So i have this function that i want to run once when the app start. This function task is to create userId then i will run another function to fetch data from firebase with the userId that created before. But the fetch function didn't start or it didnt do the task well, there is no sign of error, that's what make it more confusing. If i press the fetch function by button it work correctly.
the state
const [task, setTask] = useState(); // bisa di sebut sebagai controller text input
const [taskItems, setTaskItems] = useState([]); // state untuk list task
const [userId, setUserId] = useState();
const [isLoading, setIsLoading] = useState(true);
const baseUrl =
'https://react-http-post-RANDOM_KEY-default-rtdb.firebaseio.com/task/' + userId;
this is function to create userId function on init app
const handleCreateUser = async () => {
setIsLoading(true);
try {
const value = await AsyncStorage.getItem('userId');
if (value !== null) {
setUserId(value);
} else {
const uniqueId = makeid(6);
await AsyncStorage.setItem('userId', 'user' + uniqueId);
setUserId('user' + uniqueId);
}
await fetchDatabase();
} catch (error) {
console.log('errorrr AsyncStorage' + error);
}
setIsLoading(false);
};
this is function to fetch data from firebase
const fetchDatabase = async () => {
console.log('infinite looping');
try {
const response = await fetch(baseUrl + '.json');
if (!response.ok) {
throw new Error('Something went wrong!');
}
const data = await response.json();
// looping Map/Object dengan key sebagai indexnya
const loadedTask = [];
for (var id in data) {
loadedTask.push({
key: id,
text: data[id].text,
isComplete: data[id].isComplete,
});
}
setTaskItems(loadedTask);
} catch (error) {
setError(error.message);
}
};
this is how i call the useEffect
useEffect(() => {
handleCreateUser();
}, []);

The first thing I see is that you are not using await correctly. It should be before fetchDatabase(); function that is inside handleCreateUser like so:
await fetchDatabase();
The word await is there when you have to call an asynchronous function and you have to wait for this function to be completed.
Edit
To use only one useEffect you can check if your fetch function received your data by:
// or whatever statusCode you get when the data are present
if(reponse.statusCode === 200) {
// the await is not needed because it is present for the reponse abov
const data = response.json();
// looping Map/Object dengan key sebagai indexnya
const loadedTask = [];
for (var id in data) {
loadedTask.push({
key: id,
text: data[id].text,
isComplete: data[id].isComplete,
});
}
setTaskItems(loadedTask);
}

i got the answer, by using 2 useEffect
useEffect(() => {
handleCreateUser();
}, []);
useEffect(() => {
fetchDatabase();
}, [userId]);

Related

State Not Finished Setting before being used in useEffect

I am hosting a react app in aws amplify using the aws-serverless version of express as the REST API, which sits inside of a lambda function. A big problem that I am facing is that asynchronous jobs in aws-serverless express cause the lambda function to complete before the promises resolve. Leaving me with no data and no error handling. This caused me to bring a lot of the asynchronous work to the front end of the application.
The problem here is that I need to bring a large amount of data into state. Right now, I am using a delay workaround (shown below) but instead need a programatic way to make sure state is finished updating before being used in the second useEffect hook (dependent on odds & failedTries props) instead of using the delay functionality.
Any help would be greatly appreciated.
const App = ({ signOut }) => {
const [odds, setOdds] = useState([]);
const [updateTime,setUpdateTime] = useState(0);
const [failedTries,setFailedTries] = useState(0);
useEffect(() => {
const setNflOdds = async () => {
let response = await updateNflOdds();
let data = response;
setOdds(data);
};
setNflOdds();
setUpdateTime(1);
const interval = setInterval(() => {
setNflOdds();
setUpdateTime(updateTime => updateTime +1);
}, 100000);
return () => clearInterval(interval);
}, []);
useEffect(() => {
const s3Push = (() => {
if(!odds.length) {
setFailedTries(failedTries => failedTries + 1);
} else {
const delay = ms => new Promise(res => setTimeout(res, ms));
const nflOddsRefDelay = async() => {
*//This is the current workaround, wait ten seconds before pushing odds state up to the s3 bucket*
await delay(10000);
oddsS3Helper(odds);
};
nflOddsRefDelay()
}
});
s3Push();
}, [odds, failedTries]);
With the above indicated delay workaround this works for my use case (13k records inside of the array) but the data size is highly variable and I want to figure out a way that no matter the data size it brings the entire call up to the s3 bucket.
below is the content of the functions being called in the useEffect hook
const pushToS3 = async ( file, key ) => {
const creds = await Auth.currentCredentials()
const REGION = {region};
const s3Client = new S3Client({
credentials: Auth.essentialCredentials(creds),
region: REGION
});
const params = {
Bucket: {s3 bucket name}
Key: key,
Body: file,
};
s3Client.send(new PutObjectCommand(params));
console.log("file is sent");
};
const oddsS3Helper = (async (odds) => {
console.log("inside s3 helper: ",odds);
let csv = '';
let headers = Object.keys(odds[0]).join(',');
let values = odds.map(odd => Object.values(odd).join(',')).join('\n');
csv += headers + '\n' + values;
const buffedFile = csv;
const key = 'nflprops.csv'
const delay = ms => new Promise(res => setTimeout(res, ms));
const propRefDelay = async() => {
await delay(5000);
await postNflOdds();
};
pushToS3( buffedFile, key );
await propRefDelay();
});
async function getNflGames() {
const apiName = {name of serverless API inside of lambda};
const path = {path name};
const init = {
headers: {} // OPTIONAL
};
const data = await API.get(apiName, path, init);
return data;
};
async function getNflOdds(gameId) {
const apiName = {name of serverless API inside of lambda};
const path = {path name};
const init = {
headers: {}, // OPTIONAL
body: { gameId }
};
const data = await API.post(apiName, path, init);
return data;
};
async function updateNflOdds() {
const ojNflGames = await getNflGames();
const nflGameProps = [];
const nflOddsPush = ( async () => {
try {
await ojNflGames.data.map( async (game) => {
const ojNflOdds = await getNflOdds(game.id)
await ojNflOdds.data[0].odds.map((line) => {
nflGameProps.push(
{
gameId: game.id,
oddsId: line.id,
sports_book_name: line.sports_book_name,
name: line.name,
price: line.price,
checked_date: line.checked_date,
bet_points: line.bet_points,
is_main: line.is_main,
is_live: line.is_live,
market_name: line.market_name,
home_rotation_number: line.home_rotation_number,
away_rotation_number: line.away_rotation_number,
deep_link_url: line.deep_link_url,
player_id: line.player_id,
}
);
});
});
} catch (err) {
console.log("there was an error", err);
}
});
try {
await nflOddsPush();
} catch(err) {
console.log("odds push errored: ", err);
}
console.log("inside of updateNflOdds function: ",nflGameProps);
return nflGameProps;
};

How to check if a certain key name exist in json object in React app

I create this custom hook in my React app. It should return a boolean.
const useFetchResponse = (url: string) => {
const [isValid, setIsValid] = useState<boolean>(false);
useEffect(() => {
const fetchResponse = async () => {
const response = await fetch(url);
console.log(response);
const obj = await response.json();
if (response.ok) {
console.log(await response.json());
setIsValid(true);
}
return response;
};
fetchResponse().then((res) => res);
}, []);
return isValid;
};
export default useFetchResponse;
When I log const obj = await response.json(); it returns: {"keyName":"some=key"}.
How do I create a condition to check if response.json() has a key named keyName?
Is that for example console.log('keyName' in obj) // true?
Do you see more things which I can improve and refactor?
Let assume you get response as follow
let response = {
a:'data1',
b:'data2',
c:'data3'
};
Then you can extract keys from object as below:
let keyOnly = Object.keys(response)); // output will be ["a","b","c"]
then you can check if your require value includes on above array or not as below: Assuming if you want to check if "b" is included or not
let checkKey = keyOnly.includes(b)
if you want to check whether an object has a certain property or not, the in operator is fine.
const obj = { a: 1 };
'a' in obj // return true
'b' in obj // return false
About improvements
it's better to save all fetch states, not only valid or not. And you should wrap request with try/catch block. For example:
const [fetchState, setFetchState] = useState('pending');
useEffect(() => {
const fetchResponse = async () => {
try {
setFetchState('loading');
const response = await fetch(url);
console.log(response);
const obj = await response.json();
if (response.ok) {
console.log(await response.json());
setFetchState('success');
}
return response;
} catch (error) {
setFetchState('failed')
}
};
fetchResponse().then((res) => res);
}, []);
return fetchState;
};
fetchResponse(); would be enough. fetchResponse().then((res) => res); is unnecessary.
[optional] You could use libraries to making requests, like an axios. That would be more convenient.
in is slower than below way.
const isValid = obj[`keyname`] !== undefined
Check more detail in here

React useEffect gives react-hooks/exhaustive-deps error on publishing

My .net core react web application works fine, except that when I try to publish it gives me the following error:
Occurred while linting C:\.....Fetch.js: 79
Rule: "react-hooks/exhaustive-deps"
This is my code:
const populateTable1Data = async () => {
var response = await axios.get(apiurl + { params: { id: props.id1 } });
var data = await response.data;
setTable1Data(data);
}
const populateTable2Data = async () => {
var response = await axios.get(apiurl + { params: { id: props.id2 } });
var data = await response.data;
setTable2Data(data);
setLoading(false);
}
useEffect(() => {
const load = async () => {
await populateTable1Data();
await populateTable2Data();
setLoading(false)
}
load()
}, []);
Problem is that I have a very similar useEffect inside another component which doesn't give errors though:
const populateTableData = async () => {
const response = await axios.get(apiurl + key);
const data = await response.data;
setTableData(data);
setLoading(false);
}
useEffect(() => {
populateTableData();
}, [])
If anyone has the same problem, I solved by doing this:
const populateTable1Data = async (dataProps) => {
var response = await axios.get(apiurl + { params: { id: dataProps.id1 } });
var data = await response.data;
setTable1Data(data);
}
const populateTable2Data = async (dataProps) => {
var response = await axios.get(apiurl + { params: { id: dataProps.id2 } });
var data = await response.data;
setTable2Data(data);
setLoading(false);
}
useEffect(() => {
const load = async () => {
await populateTable1Data(props);
await populateTable2Data(props);
setLoading(false)
}
load()
}, [props]);
I essentially passed the props on the function call, I don't know why does it have to be this way, I'll leave the answer here in case anyone else needs it while waiting for someone to be kind enought to explain the reason for this.

I want my axios query 1 is finish to execute my 2nd

I would like my "userid" to no longer be null for executing my 2nd request. the code is
const [userid, setUserid] = useState(null)
const userConnected = useEffect(() => {
(async () => {
try {
const resp = await httpClient.get("//localhost:5000/#me");
setUserid(resp.data.id)
console.log(resp.data.id)
} catch (error) {
console.log("Not authenticated");
}
})();
}, []);
const fetchUserItem = async () => {
try {
const owner = await axios({
url: `${baseUrl}/additem/owner/${userid}`, //problem:
//user.id = Null
//so its like ${baseUrl}/additem/owner/null
method: 'get'
})
const { events } = owner.data
console.log(owner.data)
setitemList(events)
}
catch {
console.log('error')
}
}
i think the problem is:
as the 2 requests are carried out at the same time the {userid} = null (usestate)
so I have http://5000/additem/owner/null
i must have http://5000/additem/owner/12423
Try it, maybe this would work out:
const [userid, setUserid] = useState(null);
const userConnected = useEffect(() => {
// to run below function only when userid is null
userid == null
? (async () => {
try {
const resp = await httpClient.get("//localhost:5000/#me");
setUserid(resp.data.id);
console.log(resp.data.id);
} catch (error) {
console.log("Not authenticated");
}
})()
// call fetchUserItem inside useEffect
: (async () => {
await fetchUserItem();
})();
}, [userid] /* define userid in dependency array */);

Promise not working in React useEffect in combination with Firebase

I want to get data from firebase inside a useEffect function like this:
useEffect(() => {
/** nope */
async function fetchData() {
let dataObject = {};
let dataArray = [];
setAttendees({});
// You can await here
if (newData[listRedux]) {
const request = await Object.keys(newData[listRedux] .
[1].attendees).map(
user => {
usersRef.child(user).on('value', snap => {
dataObject[snap.key] = snap.val();
setAttendees(dataObject);
console.log(dataObject);
let comp = (
<Avatar
key={snap.key}
size="small"
source={snap.val().avatar}
alt={snap.val().name}
/>
);
dataArray.push(comp);
setAttendeesComp(dataArray);
});
}
);
// Wait for all requests, and then setState
await Promise.all(request).then(() => {
console.log('done');
});
}
}
fetchData();
}, [newData, listRedux]);
Now the second console.log inside the promise all will first show then the first console.log, meaning the request was not done yet.
How can i improve my code so the request and the states are first being set and then continue with the rest?
export default function Example() {
const [data, dataSet] = useState(false)
const [attendees, setAttendees] = useState(false)
async function fetchMyAPI() {
let response = await fetch('api/data')
response = await res.json()
console.log(response);
dataSet(response)
}
useEffect(() => {
if (!attendees) return
fetchMyAPI();
}, [attendees, newData, listRedux]);
useEffect(() => {
setAttendees({})
}, [])
More examples here:

Resources