React native, state not defined in useSelector after fetch - javascript

I am trying to load data for my component in useEffect. How can I ensure the state is defined before I go to the next screen?
const [rescueMeContent, setRescueMeContent] = useState({});
useEffect(() => {
async function fetchData() {
const { payload } = await get('xxx');
setRescueMeContent(payload);
}
fetchData();
});
When I first run the app, on the next screen when I try and access the state like this I get an error saying its undefined:
const {
content: {
splashScreen: {
backgroundImage: { url },
buttonText,
informationText,
title: pageTitle,
},
},
} = useSelector((state) => state);

useState works in inside only component. You can't reach that from other component. You must use react redux for to use useSelector. You must assign your fetch data to redux state with action. Then you can get that data with useSelector.

Related

Getting undefined from state while using useEffect and useState

My issue here is that sometimes my projects state is returning undefined sometimes. I am not sure why. As you can see, in the useEffect I have a function that gets project data from an API call to my backend server. This will then return an array of projects, which I then planned to see in the dom in the return statement. However, for whatever reason, upon the initial render it gives me an undefined and the screen goes white.
Strangely, enough, if I change the return statement to just display a regular string, let's say "hello" for example, save, and then change it back to {projects[0].name} it will then work. Yet on initial render I am getting a Uncaught TypeError: Cannot read properties of undefined (reading 'name');
I will add that I am getting a 304 status from my server in the console but that is because the data has not changed and thus I am receiving the previous UI from local storage if I remember correctly. This is not an issue with other parts of my application so I do not know why it would be an issue here.
import { useEffect, useState } from "react";
import { fetchPage } from "./../store/actions"
import { connect } from "react-redux"
/*import { ProjectCard } from "./../components/ProjectCard"*/
import API from './../api/API'
const Projects = ({ fetchPage }) => {
const [projects, setProjects] = useState([])
useEffect(() => {
const getProjectData = async () => {
try {
const { data } = await API.getAllProjects()
setProjects(data.data)
} catch (err) {
console.log(err);
}
}
fetchPage('Projects', "Here are your projects")
getProjectData()
}, [fetchPage])
return (<div>
{projects[0].name}
</div>)
}
export default connect(null, { fetchPage })(Projects);
Here is a different part of my application that works more or less the same way
const [users, setUsers] = useState([])
useEffect(() => {
const getUserData = async () => {
const { data } = await axios.get('/api/v1/users', {
headers: {
'Content-type': 'application/json'
}
})
setUsers(data.data.data)
}
fetchPage("TEAM", "Here is your team");
getUserData();
}, [fetchPage])
I tried removing the action creator which I expected did not work

Async function in UseEffect

I am trying to get data from AsyncStorage and eventually map this data to a list of buttons on my home screen. The data is saved to AsyncStorage from an API call that is made upon login.
In my async function, I am able to successfully retreive the data from AsyncStorage and parse it into JSON format, and then log it to the console. It looks like this:
{
1 : {title:"Timesheet",component_name:"Timesheet"}
2 : {title:"Personal Info",component_name:"PersonalInfo"}
3 : {title:"Employee Directory",component_name:"EmployeeListing"}
}
The problem I am running into is that I can't save this data to my useState variable and then render it into the component after useState is updated by my async function. Every time I try to access this data, I either get null or a Promise object. How can I access the data after useState is updated? Do I need to use a different React hook to call the Async function?
Here is the code that I am using:
import { Text, View, StyleSheet } from 'react-native';
import { useState, useEffect } from 'react';
import AsyncStorage from '#react-native-async-storage/async-storage';
export default function HomeScreen() {
const [buttonData, setButtonData] = useState(null);
useEffect (() => {
const loadHomeScreenButtons = async () => {
try {
const buttons = await AsyncStorage.getItem('app_screens').then(screens => {
// Parse the JSON data from its stored string format into an object.
let app_screens_json = JSON.parse(screens);
let app_screens_list = app_screens_json.app_screens;
console.log(app_screens_list); // This outputs the data to the console.
setButtonData(app_screens_list); // Trying to set the button data in useState.
return app_screens_list;
});
}
catch (e) {
console.log(e)
}
}
loadHomeScreenButtons();
}, [])
return (
<View style={home_styles.container}>
<Text>{buttonData[1]["title"]}</Text>
</View>
);
}
You just need to render a loading component until your data is fetched.
{ buttonData?.length?
<Text>{buttonData[1]["title"]}</Text> : <Text>...loading</Text>
}
You are getting an error as you are trying to access a property that does not exist at the render.
Try this way
const loadHomeScreenButtons = useCallback(async () => {
try {
const screens = await AsyncStorage.getItem('app_screens')
const app_screens_json = JSON.parse(screens)
setButtonData(app_screens_json.app_screens)
}
catch (e) {
console.log(e)
}
}, []);
useEffect(() => {
loadHomeScreenButtons();
}, [loadHomeScreenButtons]);

State not being update on app startup with zustand

The issue: On startup after the splashscreen is shown the List screen shows all users. When app is first loaded useEffect() is being called but the state update is not causing a rerender. However this only happen on startup, if I navigate on another screen and come back to it eveything happens as it should be.
const fetch = async () => {
useStore.setState({ allUsers: await getAllUsers() });
};
useEffect(() => {
fetch();
}, []);
and my zustand store is defined as below :
type Store = {
allUsers: User[];
};
export const useStore = create<Store>((set) => ({
allUsers: [],
}));
The useEffect has no dependency to trigger a re-render. useEffect with no dependency array will be only called once when the component mounts.
If the desired effect is to display updated data after fetch() is complete add the state as a dependency to the useEffect hook.
Like so:
useEffect(() => {
fetch()
},[])
Change to:
useEffect(() => {
fetch()
}, [useMazlsStore]);
For more information on how useEffect lifecycles work chekc out this article

"Fethcing and loading" function causes "Can't perform a React state update on an unmounted component" error

I am quite new in react native and found simple solution from tutorial for fetching&loading case
export const useFetching = (callback) => {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState('');
const fetching = async(data) => {
try{
setIsLoading(true);
await callback(data);
} catch(e) {
setError(e.message);
} finally {
setIsLoading(false);
}
}
return [fetching, isLoading, error];
}
I use it with signUp func from react-navigation docs as a callback, which is supposed to change the screen to main app:
signIn: async (data) => {
const token = await AuthManager.login(data);
if (token)
dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' });
But when I call it I get "Can't perform a React state update on an unmounted component". I found out that:
The "finally" block from useFetching causes the error
Empty useEffect useEffect(() => {return () => {}},[]) inserted into login screen removes the problem
I'm wondering, is there any normal solution in this case and why useEffect helps here?
Changing state on unmounted component is illegal measure in react.
This is simple events plan in your component life cycle:
Api request sended
Component was unmounted
Api request returns with data to your app
Ooooops, api caller (in your case it is fetch function) see that component was unmounted and return error, so your app execute this block of code: catch(e) { setError(e.message); }, but there is problem - setState (setError).
finally block code executed too, so you must check is component mounted before setState.
Simple solution

Axios request keeps returning twice undefined and twice the data

I'm trying to fetch an api on a custom reactjs hook using Axios. I keep getting twice the response as undefined and after that twice as a successful fetch with the data. The undefined breaks my app.
Btw I'm fetching from the randomuser api.
import axios from "axios";
import { useState, useEffect } from "react"
export const useFetch = (url) => {
const [loading, setLoading] = useState(false);
const [data, setData] = useState([]);
const [error, setError] = useState('')
const getData = () => {
setLoading(true)
try {
axios.get(url)
.then(response => setData(response.data));
setLoading(false)
} catch (error) {
setError(error)
}
};
useEffect(() => {
getData()
}, [url])
return {loading, data, error}
}
Trying to use it here and map over it
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { useFetch } from '../custom_hooks/useFetch';
const PersonDetails = () => {
const { loading, data , error } = useFetch('https://randomuser.me/api?results=20');
const { results } = data;
const { id } = useParams();
const [person, setPerson] = useState({})
useEffect(() => {
const newPerson = results?.find(person => person.login.uuid === parseInt(id))
setPerson(newPerson)
console.log(newPerson)
}, [])
return (
<div>
{person.name.first}
</div>
)
}
export default PersonDetails
This is the thing I actually Im trying to do, but now because it is undefined, I get that cannot read properties of undefined...
When the effect runs you:
setLoading(true)
Send the Ajax request
setLoading(false)
Later, then the Ajax response arrives you:
setData(response.data)
Since you depend on loading to determine if data is set or not, it breaks.
There are two things you could do:
Move setLoading(false) inside the then callback so it doesn't get set until after you have setData(response.data)
Get rid of loading entirely and base your logic off data being undefined or having a different value.
you should define the getData function inside the useeffect or pass it in dependency array and wrap the function by usecallback to avoid unnecessary rerenders.
you should use abortcontroller in case of cancelations and to have cleanup function in useeffect. (in this case it's better to define getdata body in useeffect)
useEffect(() => {
const controller = new AbortController();
const getData = async () => {
setLoading(true)
try {
await axios.get(url, {signal: controller.signal})
.then(response => setData(response.data));
} catch (error) {
setError(error)
}
}
getData()
return()=>controller.abort()
},[url]}
you can read more about fetching data with hooks in following url and where to setloading and other needed states.
https://www.robinwieruch.de/react-hooks-fetch-data/
Just in case, this solution helped me : https://github.com/axios/axios/issues/2825#issuecomment-883635938
"The problem in my case was caused by React development server.
The strict mode in react caused the issue!
I had to remove the strict mode
This solved the problem of sending double requests!
The strict mode checks are only run in development mode.
Doc: https://reactjs.org/docs/strict-mode.html
"

Categories

Resources