How do I run one function after another one in React? - javascript

In my code, I have loadData() and useFilter(). I tried to run loadData() first as productsData need to be set for useFilter() to process productsData. However, I tried .then() but it still gives me undefined when I console log useFilter() How do I fix this?
const [productsData, setProductsData] = useState(null);
const loadData = async () => {
const { data } = await getAxios().get('/market/list').then(response => response.data)
setProductsData(data)
}
const useFilter = () => {
return productsData.map(data => (...))
//filters productsData
};
useEffect(()=>{
loadData().then(()=>useFilter());
},[])

Changing state asynchronous. So using state immediately after changing that may not be updated one.
You can use useEffect to call your function after state changed.
You can do this:
const [productsData, setProductsData] = useState(null);
const useFilter = () => {
return productsData.map(data => (...))
//filters productsData
};
useEffect(()=>{
const loadData = async () => {
const { data } = await getAxios().get('/market/list').then(response => response.data)
setProductsData(data)
}
loadData()
},[])
useEffect(()=>{
useFilter();
},[productsData])

Hello just by making a function async doesn't make it return a promise in order to use .then on function calls you must make sure they return promises
So one implementation you could do is :
const [productsData, setProductsData] = useState(null);
const loadData = () => {
return getAxios().get('/market/list').then(response => response.data)
}
const useFilter = () => {
return productsData.map(data => (...))
//filters productsData
};
useEffect(()=>{
loadData().then((data)=>{
setProductsData(data);
useFilter();
}
);
},[])
In the above implementation I return the promise that axios returns you can also return your own promise by creating a promise using new Promise() constructor
Do let me know if you face any issues

Related

React Native - let return() wait until variable is defined

I am getting data in an asynchronous way and consequently showing it inside the render() function using {data}.
Now my question is, how do I let the render() function wait until the variable is defined? Currently either the placeholder variable doesn't change or it never arrives.
This is my code,
let [data] = useState();
let storeData = async (value) => {
try {
await AsyncStorage.setItem('locatie', value)
} catch (e) {
console.log(e)
}
}
let getInfo = async () => {
data = await AsyncStorage.getItem('locatie');
return data;
}
useEffect(() => {
getInfo().then(r => console.log('getInfo called, data var = ' + data))
})
console.log('return() initiate, data var = ' + data)
And showing it here:
<TextInput value={data !== undefined ? data : 'placeholder'} onChangeText={(value) => storeData(value)}/>
Evidently, since it's asynchronous, render() happens before the function.
Any help would be appreciated. Please no flame I use React Native first time :)
Also, please note that it's inside a function, I am not using classes and will not use them.
You should learn more about using react hooks. Anyway, you can refer to below for your issue.
const [data, setData] = useState('');
const storeData = async (value) => {
try {
setData(value);
await AsyncStorage.setItem('locatie', value);
} catch (e) {
console.log(e);
}
};
const getInfo = async () => {
const res = await AsyncStorage.getItem('locatie');
setData(res);
};
useEffect(() => {
getInfo();
}, []);
Instead of let [data] = useState();
write const [data, setData] = useState();
and update the state inside the useEffect hook inside a then()
in your case, after the promise returns some data.

How do I destructure a promise returned by axios?

Below is the code to fetch some data
useEffect(async (id) => {
// const data = await redditFetch(id);
async function redditFetch(id){
try {
const {data:{data}} = await axios.get(`https://www.reddit.com/search.json?q=${id}&limit=50`);
const {children} = data;
return children.map(data=> data.data);
} catch (error) {
console.log(error);
}
}
const data = redditFetch(id);
setData(data)
},[setData])
console.log(Data);
And this is the promise returned:
Promise {<pending>}
__proto__: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: Array(50)
What I want is the Array inside the PromiseResult.
I've also tried another away using async await like this which returns a proper array.
const PostPage = () => {
const {id} = useParams();
const [Data, setData] = useState([])
useEffect(async (id) => {
// const data = await redditFetch(id);
async function redditFetch(id){
try {
const {data:{data}} = await axios.get(`https://www.reddit.com/search.json?q=${id}&limit=50`);
const {children} = data;
return children.map(data=> data.data);
} catch (error) {
console.log(error);
}
}
const data = await redditFetch(id);
setData(data)
},[setData])
console.log(Data);
But with this comes a bloody warning that I can't get rid of:
index.js:1 Warning: An effect function must not return anything
besides a function, which is used for clean-up.
It looks like you wrote useEffect(async () => ...) or returned a
Promise. Instead, write the async function inside your effect and call
it immediately:
useEffect(() => { async function fetchData() {
// You can await here
const response = await MyAPI.getData(someId);
// ... } fetchData(); }, [someId]); // Or [] if effect doesn't need props or state
So, my dear coders, tell me what to do???
I did a sample demo with the post code:
CODESANDBOX: https://codesandbox.io/s/flamboyant-nobel-p87mt9?file=/src/App.js:257-265
In your code const data = redditFetch(id); the async functions will return a Promise. You have to use await, but React will warn you to not transform the useEffect callback in async function
You can remove the API function of your useEffect.
Don't put the setDate in your useEffect dependency array. In this case, you can leave this empty to run just one time. Also, don't put the date because will re-render in an infinite loop. If you need some conditional just remove the dependency array and put some conditional to run setDate.
I had to mock the id in the query parameter to be possible to work with API
CODE:
import axios from "axios";
import { useEffect, useState } from "react";
import "./styles.css";
async function redditFetch() {
try {
const {
data: { data }
} = await axios.get(`https://www.reddit.com/search.json?q=01&limit=50`);
const { children } = data;
return children.map((data) => data.data);
} catch (error) {
console.log(error);
}
}
export default function App() {
const [data, setData] = useState([]);
const Data = data.map((redditContent) => {
return (
<div key={redditContent.name}>
<h2>{redditContent.title}</h2>
<hr />
</div>
);
});
useEffect(() => {
(async function () {
const redditResponse = await redditFetch();
console.log(redditResponse);
setData(redditResponse);
})();
}, []);
return <div className="App">{Data}</div>;
}
ALTERNATIVE: CLEANER APPROACH (No anonymous function)
If you don't like of anonymous function approach, you can use this one:
async function updateRedditPosts() {
const redditResponse = await redditFetch();
console.log(redditResponse);
setData(redditResponse);
}
useEffect(() => {
updateRedditPosts();
}, []);
Anything return from useEffect supposed to be clean up while component unmount. Better you may invoke setData function there instead of return.
On the other hand, you may use immediate invoke function (IIF) then you do not need to resolve the promise that return from redditFetch function.

Using value from async function

I have some problem using async functions in functional component in React.
I have a functional component that needs to render some number.
The function getNumber : for each id, performs an API call to retrive a number (associated with the specific id).
The function createArray creates an array of those numbers out of an array of ID's (idArray).
The problem is that the return of the function(component) access value createArray() [0] , and I get en error because the type is a promise (return don't wait for the async functions).
What is the best solution for this?
const getNumber = async (id: String) => {
const numberFromApi = await getNumberFromApi(id); //returns an object
return numberFromApi.num; //taking the num value out of the object
};
const createArray = async () => {
const arrayOfNumbers= await Promise.all(
idArray.map(id => getNumber (id))
);
return arrayOfNumbers;
}
return(
<div>{createArray()[0]} </div>)
All you can really do is to read the value from your component's state, and set the state when the promise resolves:
const [theValue, setTheValue] = useState(0); // or some other initial value, you choose what's best for your component
const getNumber = async (id: String) => {
const numberFromApi = await getNumberFromApi(id); //returns an object
return numberFromApi.num; //taking the num value out of the object
};
const createArray = async () => {
const arrayOfNumbers= await Promise.all(
idArray.map(id => getNumber (id))
);
return arrayOfNumbers;
}
useEffect(async () => {
const array = await createArray();
setTheValue(array[0]);
}, []);
return(
<div>{theValue} </div>
)
You may want to alter your component to show some sort of loading indicator while waiting for the Promise to resolve.
You will need to create some state that you can update when the promises return:
// your state
const [data, setData] = useState(false);
const getNumber = async (id: String) => {
const numberFromApi = await getNumberFromApi(id); //returns an object
return numberFromApi.num; //taking the num value out of the object
};
const createArray = () => {
Promise.all(
idArray.map(id => getNumber (id))
).then(
// use setData to set whatever you want displayed
);
}
// you render your data (or nothing if there is none)
return(<div>{data} </div>)

how can i use watchPositionAsync in useFocusEffect

i tried to use this code
useFocusEffect(
useCallback(() => {
const getLocation = async() => {
await getPermissionsAsync()
const subscribe = await watchPositionAsync({
accuracy:Accuracy.BestForNavigation,
timeInterval:5000
},(location) => {console.log(1)})
return () => subscribe.remove()
}
return getLocation()
}, [])
);
but i get this error
An effect function must not return anything besides a function, which is used for clean-up. It looks like you wrote 'useFocusEffect(async () => ...)' or returned a Promise. Instead, write the
async function inside your effect and call it immediately:
useFocusEffect(
React.useCallback() => {
async function fetchData() {
// You can await here
const response = await MyAPI.getData(someId);
// ...
}
fetchData();
}, [someId])
};
TL;DR
useFocusEffect(
useCallback(() => {
let subcription;
const getLocation = async() => {
await getPermissionsAsync()
subcription = await watchPositionAsync({
accuracy:Accuracy.BestForNavigation,
timeInterval:5000
}, (location) => {console.log(1)})
}
getLocation();
return () => { subscription && subscription.remove(); }
}, [])
);
The error actually says what needs to occur. that is the return value must be a simple function used for cleanup.
The subscription is obtained as part of an async call in the getLocation function, but for it to be used it must be pulled out and have it set asynchronously.
Then the cleanup function just needs to check if it is already set and if it is invoke the remove method.

How to simplify an async call inside a React useEffect Hook

I'm trying to create the simplest React Hook useEffect call I can and hoping not to have to create a function call inside useEffect. My code is below and it requires me to call fetchData() to make it work. Is there a simpler version of this?
Maybe with a strategically placed async keyword or something like that?
useEffect(() => {
const fetchData = async () => {
let result = await axios.get('http://localhost:4000/');
console.log(result.data.length);
};
fetchData();
}, []);
What you can do is make it an IIFE:
useEffect(() => {
const promise = (async () => {
let result = await axios.get('http://localhost:4000/');
console.log(result.data.length);
})();
return (() => {
promise.then(() => cleanup());
});
}, []);
Or use plain promises:
useEffect(() => {
const promise = axios.get('http://localhost:4000/').then((result) => {
console.log(result.data.length);
});
return (() => {
promise.then(() => cleanup());
})
}, []);
But that is about as much as you can do since useEffect cannot be async directly. From ESLint:
Effect callbacks are synchronous to prevent race conditions.

Categories

Resources