Warning: Cannot update a component from inside the function body of a different component in React Native - reactjs

i have loading screen for call all the data function.i used async function for all function call.
//NOTE: this screen loads all the data and store it in store so user will have a smother experience
const LoadingScreen = (props) => {
const gotToHomeScreen = () => {
props.navigation.replace("Home", { screen: HOME_SCREEN });
};
//NOTE: loading data here for home screen journey
const getRequiredAPIDataInStore = async () => {
GetAllFieldProp();
GetAllSalaryAPIResponse();
GetSalaryAPIResponse();
let { spinnerStateForm101 } = GetForm101API();
let { spinnerStateForm106 } = GetForm106API();
GetMessagesCountAPI();
GetMessagesAPI(props);
GetAllFormAPIResponse();
GetAllSpecificSalaryAPIResponse();
let { spinnerStateMonthly } = GetMonthlyAbsenceAPI(props);
let { spinnerStateWeekly } = GetWeeklyAbsenceAPI(props);
if (
spinnerStateMonthly &&
spinnerStateWeekly &&
spinnerStateForm106 &&
spinnerStateForm101
) {
gotToHomeScreen();
}
};
getRequiredAPIDataInStore();
export default LoadingScreen;
but i am getting warning messages for this.
Warning: Cannot update a component from inside the function body of a different component.
at src/screens/loading-screen.js:19:26 in gotToHomeScreen
at src/screens/loading-screen.js:37:6 in getRequiredAPIDataInStore
How to solve this warning messsage?

Here's the approach I would take.
const Loading = () => {
const [spinnerStateMonthly, setSpinnerStatMonthly] = useState(null);
const [spinnerStateWeekly, setspinnerStateWeekly] = useState(null);
const [spinnerStateForm106, setspinnerStateForm106] = useState(null);
const [spinnerStateForm101, setSpinnerStateForm101] = useState(null);
const gotToHomeScreen = () => {
props.navigation.replace("Home", { screen: HOME_SCREEN });
};
useEffect(() => {
// async callback to get all the data and set state
(async () => {
await GetAllFieldProp();
await GetAllSalaryAPIResponse();
await GetSalaryAPIResponse();
const { spinnerStateForm101: local101 } = GetForm101API();
const { spinnerStateForm106: local106 } = GetForm106API();
setSpinnerStateForm101(local101);
setSpinnerStateForm106(local106);
await GetMessagesCountAPI();
await GetMessagesAPI(props);
await GetAllFormAPIResponse();
await GetAllSpecificSalaryAPIResponse();
const { spinnerStateMonthly: localMonthly } = GetMonthlyAbsenceAPI(props);
const { spinnerStateWeekly: localWeekly } = GetWeeklyAbsenceAPI(props);
setSpinnerStateMonthly(localMonthly);
setSpinnerStateWeekly(localWeekly);
})();
}, []);
// effect to check for what the state is and if all the states are satisfied
// then go to the home screen
useEffect(() => {
if (spinnerStateMonthly
&& spinnerStateWeekly
&& spinnerStateForm106
&& spinnerStateForm101) {
gotToHomeScreen();
}
}, [spinnerStateMonthly, spinnerStateWeekly, spinnerStateForm101,
spinnerStateForm106]);
};

Related

How to immediately update a state in React

I'm struggling with this problem and I've already tried many solutions but none of them fit me.
I have a context that I use to share information that I get from an API. I will summarize the files for you:
file: useGetInfo.tsx
type InfoContextData = { ... }
type Props = { ... }
type InfoResponseProps = { ... }
export const InfoContext = createContext<InfoContextData>({} as InfoContextData)
export const InformationProvider = ({ children }: Props) => {
const isBrowser = typeof window !== `undefined`
const [infoStorage, setInfoStorage] = useState(
isBrowser && localStorage.getItem('info')
? String(localStorage.getItem('info'))
: undefined
)
const [result, setResult] = useState<InfoResponseProps | null>(null)
const getInfo = useCallback(async (value: string) => {
const url = `<URL_FROM_API${value}>`
await axios.get(url)
.then((response) => {
setResult(response.data)
})
.catch((_) => {
setResult(null)
})
})
useEffect(() => {
if (!infoStorage) {
return
}
getInfo(infoStorage)
}, [infoStorage, getInfo])
return (
<InfoContext.Provider
value={{
result,
setResult,
infoStorage,
setInfoStorage,
getInfo,
}}
>
{children}
</InfoContext.Provider>
)
}
Then in the component I call the context:
file: SomeComponent.tsx
const Component = () => {
const { setInfoStorage, getInfo, result } = useContext(InfoContext)
const [input, setInput] = useState('')
const handleInfoSubmit = useCallback(() => {
getInfo(input)
if (!result || !result?.ok) {
localStorage.removeItem('info')
setInfoStorage(undefined)
}
setInfoStorage(input)
localStorage.setItem('info', 'input')
setInput('')
}, [input, result, getInfo, setInfoStorage, setInput])
return (
...
<Form onSubmit={handleInfoSubmit}>
<input>
...
</Form>
)
}
Basically, the user inserts a code in the form and when he submits the form, it runs the handleInfoSubmit function. Then, the code runs the function getInfo() and after requesting the API it returns the information to the state result.
The problem is in the SomeComponent.tsx file: when I run the function getInfo(input) I need the information in the state result but at the time axios finishes the request to the API and the code goes to the if (!result || !result?.ok) line, the result state is not still fulfilled.
I know that React/Gatsby can't update immediately the state like what I need, but is there a way to overcome this problem? Thanks in advance.
I think the value of the result would always be stale inside the handleInfoSubmit function per your code.
Rewrite the getInfo and handleInfoSubmit like this
// Return data from getInfo so that we can use the value directly in handleInfoSubmit
const getInfo = useCallback(async (value: string) => {
const url = `<URL_FROM_API${value}>`
try {
const { data } = await axios.get(url);
setResult(data)
return data;
} catch {
setResult(null)
}
return null;
})
const handleInfoSubmit = useCallback(async () => {
// await getInfo and get the axios response data.
const result = await getInfo(input)
if (!result || !result?.ok) {
localStorage.removeItem('info')
setInfoStorage(undefined)
}
setInfoStorage(input)
localStorage.setItem('info', 'input')
setInput('')
}, [input, getInfo, setInfoStorage, setInput])

React. How to make function with state?

I'm learning React Native and can't understand how to make function or component instead of repeated part of my code:
export const PostScreen = () => {
const postId = 1
/* THIS PART IS REPEATED IN MANY FILES */
const [data, setData] = useState([]);
const [loader, setLoader] = useState(false);
const [error, setError] = useState(null);
const fetchData = async () => {
setError(null)
setLoader(true)
try {
const data = await Api.get(`http://localhost/api/posts/${postId}`)
if (data.success == 1) {
setData(data.data)
}
else {
setError(data.error)
}
}
catch(e) {
console.log(e)
setError('Some problems')
}
finally {
setLoader(false)
}
}
useEffect(() => {
fetchData()
}, [])
if (loader) {
return <Loader />
}
if (error) {
return <View><Text>{error}</Text></View>
}
/*END>>> THIS PART IS REPEATED IN MANY FILES */
return (
<View><Text>{data.title}</Text></View>
)
}
The problem is that fetchData is working with state. I found, how to do it with Context, but I don't wont to use it. Is there any way to do clear code without Context?
So, why not make your own hook, then?
Write the hook in a dedicated module:
function useMyOwnHook(props) {
const {
postId,
} = props;
const [data, setData] = useState([]);
const [loader, setLoader] = useState(false);
const [error, setError] = useState(null);
const fetchData = async () => {
setError(null)
setLoader(true)
try {
const data = await Api.get(`http://localhost/api/posts/${postId}`)
if (data.success == 1) {
setData(data.data)
}
else {
setError(data.error)
}
}
catch(e) {
console.log(e)
setError('Some problems')
}
finally {
setLoader(false)
}
}
useEffect(() => {
fetchData()
}, [])
const render = loader
? <Loader />
: error
? <View><Text>{error}</Text></View>
: null;
return {
data,
render,
}
}
At that point, the component will be written as follows:
export const PostScreen = () => {
const postId = 1
const {
render,
data,
} = useMyOwnHook({ postId });
return render ?? (
<View><Text>{data.title}</Text></View>
)
}

React component is not loading props after history.goback()

I have a component which is updating states on page load with useeffect. But when i use history.goback() function to go back from a component to my bank component useeffect is not working. Here my code ;
bottomnavigator.js :
const goBack = () => {
if (props.history.location.pathname !== "/app/phone" && props.history.location.pathname !== "/") {
props.history.goBack();
}
};
bank.js :
const [transactions, settransactions] = useState([]);
const [bankBalance, setbankBalance] = useState(0);
const getBankData = async () => {
if (props.location.state) {
const res = await Nui.post(props.location.state, {});
if (res) {
setbankBalance(res);
}
}
}
const getTransactionData = async () => {
if (props.location.state) {
const res = await Nui.post('GET_BANK_TRANSACTIONS', {});
if (res) {
settransactions(res);
}
}
}
useEffect(() => {
getBankData();
getTransactionData();
}, []);
The issue is that you have listed "no dependencies" ([]) in your useEffect, so it will run once at the beginning and then never again. You should either omit the dependencies like so:
useEffect(() => {
getBankData();
getTransactionData();
});
Or list the correct set of dependencies like so: (I'm guessing here based on your code, but hopefully I got it right)
useEffect(() => {
getBankData();
getTransactionData();
}, [props.location.state]);

Take photo and video with same component react native

My goal is to be able to take a photo when tapped, and a video when pressed down and held.
Currently, my code takes a photo. But when I try trigger a recording event with onLongPress, I can't seem to get it to work.
Any ideas what I'm doing wrong?
const onLongPressButton = () => {
setLongPressed(true);
};
useEffect(() => {
if (longPressed == true) {
const startRecord = async () => {
console.log("RECORDING");
if (cameraRef.current) {
setRecording(true);
const recordedVideo = await cameraRef.current.recordAsync();
setVideo(recordedVideo);
setLongPressed(true);
}
};
startRecord();
}
}, [longPressed]);
const stopRecord = async () => {
setLongPressed(false);
console.log("STOP RECORDING");
setRecording(false);
await cameraRef.current.stopRecording();
};
const handlePhoto = async () => {
console.log("Photo");
if (cameraRef.current) {
let photo = await cameraRef.current.takePictureAsync({});
console.log(photo.uri);
}
};
And here is my component:
<Circle
onPress={handlePhoto}
onLongPress={onLongPressButton}
onPressOut={stopRecord}
delayLongPress={200}
/>

How can I initialize in useState with the data from custom hooks?

I'm learning to React Hooks.
And I'm struggling initialize data that I fetched from a server using a custom hook.
I think I'm using hooks wrong.
My code is below.
const useFetchLocation = () => {
const [currentLocation, setCurrentLocation] = useState([]);
const getCurrentLocation = (ignore) => {
...
};
useEffect(() => {
let ignore = false;
getCurrentLocation(ignore);
return () => { ignore = true; }
}, []);
return {currentLocation};
};
const useFetch = (coords) => {
console.log(coords);
const [stores, setStores] = useState([]);
const fetchData = (coords, ignore) => {
axios.get(`${URL}`)
.then(res => {
if (!ignore) {
setStores(res.data.results);
}
})
.catch(e => {
console.log(e);
});
};
useEffect(() => {
let ignore = false;
fetchData(ignore);
return () => {
ignore = true;
};
}, [coords]);
return {stores};
}
const App = () => {
const {currentLocation} = useFetchLocation();
const {stores} = useFetch(currentLocation); // it doesn't know what currentLocation is.
...
Obviously, it doesn't work synchronously.
However, I believe there's the correct way to do so.
In this case, what should I do?
I would appreciate if you give me any ideas.
Thank you.
Not sure what all the ignore variables are about, but you can just check in your effect if coords is set. Only when coords is set you should make the axios request.
const useFetchLocation = () => {
// Start out with null instead of an empty array, this makes is easier to check later on
const [currentLocation, setCurrentLocation] = useState(null);
const getCurrentLocation = () => {
// Somehow figure out the current location and store it in the state
setTimeout(() => {
setCurrentLocation({ lat: 1, lng: 2 });
}, 500);
};
useEffect(() => {
getCurrentLocation();
}, []);
return { currentLocation };
};
const useFetch = coords => {
const [stores, setStores] = useState([]);
const fetchData = coords => {
console.log("make some HTTP request using coords:", coords);
setTimeout(() => {
console.log("pretending to receive data");
setStores([{ id: 1, name: "Store 1" }]);
}, 500);
};
useEffect(() => {
/*
* When the location is set from useFetchLocation the useFetch code is
* also triggered again. The first time coords is null so the fetchData code
* will not be executed. Then, when the coords is set to an actual object
* containing coordinates, the fetchData code will execute.
*/
if (coords) {
fetchData(coords);
}
}, [coords]);
return { stores };
};
function App() {
const { currentLocation } = useFetchLocation();
const { stores } = useFetch(currentLocation);
return (
<div className="App">
<ul>
{stores.map(store => (
<li key={store.id}>{store.name}</li>
))}
</ul>
</div>
);
}
Working sandbox (without the comments) https://codesandbox.io/embed/eager-elion-0ki0v

Resources