Change loading state after function call - javascript

I have 3 API calls in a function fetchData. How do I set the loading state to false after executing the following code:
componentWillMount() {
this.setState({ loading: true })
this.fetchData()
//after these two calls, I want to set the loading State to false
}
All the API calls are each a promise
Using React 14.3

If you're using Redux you could have your reducer set loading: false after the API calls return.
See for more info

You should make the API calls on componentDidMount as componentWillMount is set to be deprecated.
FetchData() {
APIcalls.then(() => {
This.setState({ loading: false })
})
}

You would make use of async-await and use componentDidMount instead of componentWillMount since this method is likely to be replaced in react v17
Also check this question
Use componentWillMount or componentDidMount lifecycle functions for async request in React
async componentDidMount() {
this.setState({ loading: true })
await this.fetchData()
this.setState({ loading: false });
}
async fetchData() {
await ApiCall1();
await ApiCall2();
await ApiCall3();
}

I used es6 async await function. Inside that, You can use Promise.all to resolve the all promises.
fetchData = async () => {
const a = new Promise((resolve, reject) => {
setTimeout(() => { console.log('one'); return resolve("done!"); }, 1000)
});
const b = new Promise((resolve, reject) => {
setTimeout(() => { console.log('two'); return resolve("done!"); }, 2000)
});
return await Promise.all([a, b]) ? true : false;
}
Write the logic in componentDidMount instead of componentWillMount. you can use fetchData function as a
promise. After that you can set state whatever you want.
componentDidMount() {
this.setState({ loading: true })
this.fetchData().then((result)=>{
console.log(result, 'three');
this.setState({ loading: false });
});
}
You can see working example click here

Related

useEffect must not return anything beside a function, which is used for clean-up Error Comes up Every Screen [duplicate]

I was trying the useEffect example something like below:
useEffect(async () => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
const json = await response.json();
setPosts(json.data.children.map(it => it.data));
} catch (e) {
console.error(e);
}
}, []);
and I get this warning in my console. But the cleanup is optional for async calls I think. I am not sure why I get this warning. Linking sandbox for examples. https://codesandbox.io/s/24rj871r0p
For React version <=17
I suggest to look at Dan Abramov (one of the React core maintainers) answer here:
I think you're making it more complicated than it needs to be.
function Example() {
const [data, dataSet] = useState<any>(null)
useEffect(() => {
async function fetchMyAPI() {
let response = await fetch('api/data')
response = await response.json()
dataSet(response)
}
fetchMyAPI()
}, [])
return <div>{JSON.stringify(data)}</div>
}
Longer term we'll discourage this pattern because it encourages race conditions. Such as — anything could happen between your call starts and ends, and you could have gotten new props. Instead, we'll recommend Suspense for data fetching which will look more like
const response = MyAPIResource.read();
and no effects. But in the meantime you can move the async stuff to a separate function and call it.
You can read more about experimental suspense here.
If you want to use functions outside with eslint.
function OutsideUsageExample({ userId }) {
const [data, dataSet] = useState<any>(null)
const fetchMyAPI = useCallback(async () => {
let response = await fetch('api/data/' + userId)
response = await response.json()
dataSet(response)
}, [userId]) // if userId changes, useEffect will run again
useEffect(() => {
fetchMyAPI()
}, [fetchMyAPI])
return (
<div>
<div>data: {JSON.stringify(data)}</div>
<div>
<button onClick={fetchMyAPI}>manual fetch</button>
</div>
</div>
)
}
For React version >=18
Starting with React 18 you can also use Suspense, but it's not yet recommended if you are not using frameworks that correctly implement it:
In React 18, you can start using Suspense for data fetching in opinionated frameworks like Relay, Next.js, Hydrogen, or Remix. Ad hoc data fetching with Suspense is technically possible, but still not recommended as a general strategy.
If not part of the framework, you can try some libs that implement it like swr.
Oversimplified example of how suspense works. You need to throw a promise for Suspense to catch it, show fallback component first and render Main component when promise it's resolved.
let fullfilled = false;
let promise;
const fetchData = () => {
if (!fullfilled) {
if (!promise) {
promise = new Promise(async (resolve) => {
const res = await fetch('api/data')
const data = await res.json()
fullfilled = true
resolve(data)
});
}
throw promise
}
};
const Main = () => {
fetchData();
return <div>Loaded</div>;
};
const App = () => (
<Suspense fallback={"Loading..."}>
<Main />
</Suspense>
);
When you use an async function like
async () => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
const json = await response.json();
setPosts(json.data.children.map(it => it.data));
} catch (e) {
console.error(e);
}
}
it returns a promise and useEffect doesn't expect the callback function to return Promise, rather it expects that nothing is returned or a function is returned.
As a workaround for the warning you can use a self invoking async function.
useEffect(() => {
(async function() {
try {
const response = await fetch(
`https://www.reddit.com/r/${subreddit}.json`
);
const json = await response.json();
setPosts(json.data.children.map(it => it.data));
} catch (e) {
console.error(e);
}
})();
}, []);
or to make it more cleaner you could define a function and then call it
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
`https://www.reddit.com/r/${subreddit}.json`
);
const json = await response.json();
setPosts(json.data.children.map(it => it.data));
} catch (e) {
console.error(e);
}
};
fetchData();
}, []);
the second solution will make it easier to read and will help you write code to cancel previous requests if a new one is fired or save the latest request response in state
Working codesandbox
Until React provides a better way, you can create a helper, useEffectAsync.js:
import { useEffect } from 'react';
export default function useEffectAsync(effect, inputs) {
useEffect(() => {
effect();
}, inputs);
}
Now you can pass an async function:
useEffectAsync(async () => {
const items = await fetchSomeItems();
console.log(items);
}, []);
Update
If you choose this approach, note that it's bad form. I resort to this when I know it's safe, but it's always bad form and haphazard.
Suspense for Data Fetching, which is still experimental, will solve some of the cases.
In other cases, you can model the async results as events so that you can add or remove a listener based on the component life cycle.
Or you can model the async results as an Observable so that you can subscribe and unsubscribe based on the component life cycle.
You can also use IIFE format as well to keep things short
function Example() {
const [data, dataSet] = useState<any>(null)
useEffect(() => {
(async () => {
let response = await fetch('api/data')
response = await response.json()
dataSet(response);
})();
}, [])
return <div>{JSON.stringify(data)}</div>
}
void operator could be used here.
Instead of:
React.useEffect(() => {
async function fetchData() {
}
fetchData();
}, []);
or
React.useEffect(() => {
(async function fetchData() {
})()
}, []);
you could write:
React.useEffect(() => {
void async function fetchData() {
}();
}, []);
It is a little bit cleaner and prettier.
Async effects could cause memory leaks so it is important to perform cleanup on component unmount. In case of fetch this could look like this:
function App() {
const [ data, setData ] = React.useState([]);
React.useEffect(() => {
const abortController = new AbortController();
void async function fetchData() {
try {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
const response = await fetch(url, { signal: abortController.signal });
setData(await response.json());
} catch (error) {
console.log('error', error);
}
}();
return () => {
abortController.abort(); // cancel pending fetch request on component unmount
};
}, []);
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
I read through this question, and feel the best way to implement useEffect is not mentioned in the answers.
Let's say you have a network call, and would like to do something once you have the response.
For the sake of simplicity, let's store the network response in a state variable.
One might want to use action/reducer to update the store with the network response.
const [data, setData] = useState(null);
/* This would be called on initial page load */
useEffect(()=>{
fetch(`https://www.reddit.com/r/${subreddit}.json`)
.then(data => {
setData(data);
})
.catch(err => {
/* perform error handling if desired */
});
}, [])
/* This would be called when store/state data is updated */
useEffect(()=>{
if (data) {
setPosts(data.children.map(it => {
/* do what you want */
}));
}
}, [data]);
Reference => https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
For other readers, the error can come from the fact that there is no brackets wrapping the async function:
Considering the async function initData
async function initData() {
}
This code will lead to your error:
useEffect(() => initData(), []);
But this one, won't:
useEffect(() => { initData(); }, []);
(Notice the brackets around initData()
For fetching from an external API using React Hooks, you should call a function that fetches from the API inside of the useEffect hook.
Like this:
async function fetchData() {
const res = await fetch("https://swapi.co/api/planets/4/");
res
.json()
.then(res => setPosts(res))
.catch(err => setErrors(err));
}
useEffect(() => {
fetchData();
}, []);
I strongly recommend that you do not define your query inside the useEffect Hook, because it will be re-render infinite times. And since you cannot make the useEffect async, you can make the function inside of it to be async.
In the example shown above, the API call is in another separated async function so it makes sure that the call is async and that it only happens once. Also, the useEffect's dependency array (the []) is empty, which means that it will behave just like the componentDidMount from React Class Components, it will only be executed once when the component is mounted.
For the loading text, you can use React's conditional rendering to validate if your posts are null, if they are, render a loading text, else, show the posts. The else will be true when you finish fetching data from the API and the posts are not null.
{posts === null ? <p> Loading... </p>
: posts.map((post) => (
<Link key={post._id} to={`/blog/${post.slug.current}`}>
<img src={post.mainImage.asset.url} alt={post.mainImage.alt} />
<h2>{post.title}</h2>
</Link>
))}
I see you already are using conditional rendering so I recommend you dive more into it, especially for validating if an object is null or not!
I recommend you read the following articles in case you need more information about consuming an API using Hooks.
https://betterprogramming.pub/how-to-fetch-data-from-an-api-with-react-hooks-9e7202b8afcd
https://reactjs.org/docs/conditional-rendering.html
try
const MyFunctionnalComponent: React.FC = props => {
useEffect(() => {
// Using an IIFE
(async function anyNameFunction() {
await loadContent();
})();
}, []);
return <div></div>;
};
Other answers have been given by many examples and are clearly explained, so I will explain them from the point of view of TypeScript type definition.
The useEffect hook TypeScript signature:
function useEffect(effect: EffectCallback, deps?: DependencyList): void;
The type of effect:
// NOTE: callbacks are _only_ allowed to return either void, or a destructor.
type EffectCallback = () => (void | Destructor);
// Destructors are only allowed to return void.
type Destructor = () => void | { [UNDEFINED_VOID_ONLY]: never };
Now we should know why effect can't be an async function.
useEffect(async () => {
//...
}, [])
The async function will return a JS promise with an implicit undefined value. This is not the expectation of useEffect.
Please try this
useEffect(() => {
(async () => {
const products = await api.index()
setFilteredProducts(products)
setProducts(products)
})()
}, [])
To do it properly and avoid errors: "Warning: Can't perform a React state update on an unmounted..."
useEffect(() => {
let mounted = true;
(async () => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
const json = await response.json();
const newPosts = json.data.children.map(it => it.data);
if (mounted) {
setPosts(newPosts);
}
} catch (e) {
console.error(e);
}
})();
return () => {
mounted = false;
};
}, []);
OR External functions and using an object
useEffect(() => {
let status = { mounted: true };
query(status);
return () => {
status.mounted = false;
};
}, []);
const query = async (status: { mounted: boolean }) => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
const json = await response.json();
const newPosts = json.data.children.map(it => it.data);
if (status.mounted) {
setPosts(newPosts);
}
} catch (e) {
console.error(e);
}
};
OR AbortController
useEffect(() => {
const abortController = new AbortController();
(async () => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`, { signal: abortController.signal });
const json = await response.json();
const newPosts = json.data.children.map(it => it.data);
setPosts(newPosts);
} catch (e) {
if(!abortController.signal.aborted){
console.error(e);
}
}
})();
return () => {
abortController.abort();
};
}, []);
I know it is late but just I had the same problem and I wanted to share that I solved it with a function like this!
useEffect(() => {
(async () => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
const json = await response.json();
setPosts(json.data.children.map(it => it.data));
} catch (e) {
console.error(e);
}
}) ()
}, [])
With useAsyncEffect hook provided by a custom library, safely execution of async code and making requests inside effects become trivially since it makes your code auto-cancellable (this is just one thing from the feature list). Check out the Live Demo with JSON fetching
import React from "react";
import { useAsyncEffect } from "use-async-effect2";
import cpFetch from "cp-fetch";
/*
Notice: the related network request will also be aborted
Checkout your network console
*/
function TestComponent(props) {
const [cancel, done, result, err] = useAsyncEffect(
function* () {
const response = yield cpFetch(props.url).timeout(props.timeout);
return yield response.json();
},
{ states: true, deps: [props.url] }
);
return (
<div className="component">
<div className="caption">useAsyncEffect demo:</div>
<div>
{done ? (err ? err.toString() : JSON.stringify(result)) : "loading..."}
</div>
<button className="btn btn-warning" onClick={cancel} disabled={done}>
Cancel async effect
</button>
</div>
);
}
export default TestComponent;
The same demo using axios
Just a note about HOW AWESOME the purescript language handles this problem of stale effects with Aff monad
WITHOUT PURESCRIPT
you have to use AbortController
function App() {
const [ data, setData ] = React.useState([]);
React.useEffect(() => {
const abortController = new AbortController();
void async function fetchData() {
try {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
const response = await fetch(url, { signal: abortController.signal });
setData(await response.json());
} catch (error) {
console.log('error', error);
}
}();
return () => {
abortController.abort(); // cancel pending fetch request on component unmount
};
}, []);
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
or stale (from NoahZinsmeister/web3-react example)
function Balance() {
const { account, library, chainId } = useWeb3React()
const [balance, setBalance] = React.useState()
React.useEffect((): any => {
if (!!account && !!library) {
let stale = false
library
.getBalance(account)
.then((balance: any) => {
if (!stale) {
setBalance(balance)
}
})
.catch(() => {
if (!stale) {
setBalance(null)
}
})
return () => { // NOTE: will be called every time deps changes
stale = true
setBalance(undefined)
}
}
}, [account, library, chainId]) // ensures refresh if referential identity of library doesn't change across chainIds
...
WITH PURESCRIPT
check how useAff kills it's Aff in the cleanup function
the Aff is implemented as a state machine (without promises)
but what is relevant to us here is that:
the Aff encodes how to stop the Aff - You can put your AbortController here
it will STOP running Effects (not tested) and Affs (it will not run then from the second example, so it will NOT setBalance(balance)) IF the error was thrown TO the fiber OR INSIDE the fiber
Ignore the warning, and use the useEffect hook with an async function like this:
import { useEffect, useState } from "react";
function MyComponent({ objId }) {
const [data, setData] = useState();
useEffect(() => {
if (objId === null || objId === undefined) {
return;
}
async function retrieveObjectData() {
const response = await fetch(`path/to/api/objects/${objId}/`);
const jsonData = response.json();
setData(jsonData);
}
retrieveObjectData();
}, [objId]);
if (objId === null || objId === undefined) {
return (<span>Object ID needs to be set</span>);
}
if (data) {
return (<span>Object ID is {objId}, data is {data}</span>);
}
return (<span>Loading...</span>);
}
The most easy way is to use useAsyncEffect from 'use-async-effect'
You can find it on NPM.
const ProtectedRoute = ({ children }) => {
const [isAuth, setIsAuth] = useState(false);
useAsyncEffect(async () => {
try {
const data = await axios("auth");
console.log(data);
setIsAuth(true);
} catch (error) {
console.log(error);
}
}, []);
if (!isAuth)
return <Navigate to="/signin" />
return children;
}

React componentDidMount setState but return undefined

I can get list with get axios.get method. then ı can use setState and it works flawlessly.
but the return is not true , its return undefined console.log(result) => undefined . How can ı check if setState work fine return true or return false ?
getList = async () => {
await axios.get("http://localhost:5000/list").then((response) => {
this.setState(
{
copy: response.data.list,
},
() => {
return true;
},
);
});
};
componentDidMount = () => {
this.getList().then((result) => {
console.log(result);
});
};
Your return true statement is in setState's post-set callback. It won't be propagated to the promise that's returned from getList; indeed, you return nothing from that function (you don't even return the Axios promise; if you did return that, you would get the response logged in your console.log, but it would be logged before the setState callback finished), so you get undefined in the console.log.
If you need getList to return a promise that resolves to true once the state has been set, you'll need
getList = () => {
return new Promise((resolve) =>
axios.get("http://localhost:5000/list").then((response) =>
this.setState(
{
copy: response.data.list,
},
() => resolve(true),
),
),
);
};
componentDidMount = () => {
this.getList().then((result) => {
console.log(result);
});
};
The second argument to set state can return true but, it's not going anywhere. You need to somehow use it, example:
const response = await axios.get("http://localhost:5000/list")
return new Promise((resolve) => {
this.setState({
copy : response.data.list
}, () => {
resolve(true);
})
})
now the chain will work because you are resolving the promise chain with true instead of returning it from the callback function

How to run JavaScript functions in order (React)

I have created a small react App that presents documents from a SharePoint site.
To make the App work correctly I am having to add setTimeouts, but I know there must be a better way using callbacks or promises or something >.<
My knowledge is lacking, so could somebody please point me in the right direction?
// Handles what runs when the drop down is changed
public handleDropdownChange(e) {
// Updates dropdown select
this.setState({ selectedOption: e.target.value, isLoading: true });
// Checks security permissions - MAKES A GET REQUEST
setTimeout(() => {
this.getSecurityGroupUsers();
}, 1000);
// Ghecks if user can access docs
setTimeout(() => {
this.checkForDocAccess();
}, 2000);
// Gets documents - MAKES A GET REQUEST
setTimeout(() => {
this.getDocuments();
}, 4000);
// Delete Mark as Reviewed property THEN displays docs
setTimeout(() => {
this.hideMarkAsReviewed();
}, 8000);
}
One of the functions:
// Grabs the documents
public getDocuments() {
axios
.get("https://bpk.sharepoint.com/_api/search/query?querytext='owstaxIdDocumentx0020Owner:" + this.state.selectedOption + "'&trimduplicates=true&rowsperpage=100&rowlimit=1000&selectproperties='LastReviewDateOWSDATE,ScheduledReviewDateOWSDATE,Path'",
{ params:{},
headers: { 'accept': 'application/json;odata=verbose' }
})
.then(response =>
response.data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results.map(document => ({
Type: this.checkFile(document),
Name: document.Cells.results[6].Value.split("/").pop(),
'Scheduled Review Date': document.Cells.results[8].Value.slice(0,-11),
Path: document.Cells.results[6].Value.replace('https://bpkintelogydev.sharepoint.com', ''),
Site: document.Cells.results[6].Value.split('/').slice(4).slice(0,1),
'Last Review Date': document.Cells.results[7].Value.slice(0,-11),
View: <a href="#" onClick={()=>window.open(document.Cells.results[6].Value + '?web=1&action=edit')}>View</a>,
'Mark as Reviewed': <a href='#'>Mark</a>
}))
)
.then(documents => {
this.setState({documents, isLoading: true});
})
.catch(error => {
//console.log(error);
});
}
Yes, callbacks or promises are what you do here. Promises would be the more modern way (not least so you can use async/await with them). setState provides a callback for when the state is set if you want that (it's an optional second argument to setState). Then your various functions (getSecurityGroupUsers, etc.) would return promises that they fulfill or reject.
In your getDocuments, for instance, you want to:
return the result of calling then, and
Don't have an error handler, leave that to handleDropdownChange
...and similar in the others. For any that don't already have a promise to chain on (getDocuments has the one from axios) because they use a callback-style API you can create a promise and fulfill/reject it yourself (see this question's answers for details there).
Doing that, handleDropdownChange might look something like:
// Handles what runs when the drop down is changed
public handleDropdownChange(e) {
// Updates dropdown select
this.setState({ selectedOption: e.target.value, isLoading: true });
this.getSecurityGroupUsers()
.then(() => this.checkForDocAccess())
.then(() => this.getDocuments())
.then(() => this.hideMarkAsReviewed())
.catch(error => {
// ...handle/report error...
});
}
or if you didn't want to start those until the state had been changed initially:
// Handles what runs when the drop down is changed
public handleDropdownChange(e) {
// Updates dropdown select
this.setState(
{ selectedOption: e.target.value, isLoading: true },
() => {
this.getSecurityGroupUsers()
.then(() => this.checkForDocAccess())
.then(() => this.getDocuments())
.then(() => this.hideMarkAsReviewed())
.catch(error => {
// ...handle/report error...
});
}
);
}
In this particular case, async/await doesn't help much, but perhaps some. I assume handleDropdownChange shouldn't be async since it's an event handler and so nothing would handle the promise it would return, so:
// Handles what runs when the drop down is changed
public handleDropdownChange(e) {
// Updates dropdown select
this.setState({ selectedOption: e.target.value, isLoading: true });
(async () => {
try {
await this.getSecurityGroupUsers();
await this.checkForDocAccess();
await this.getDocuments();
await this.hideMarkAsReviewed();
} catch (error) {
// ...handle/report error...
}
})();
}
Notice how the entire body of the async function is in a try/catch, because nothing hooks its promise, so it's important we handle errors directly. (Ensure that "handle/report error" doesn't throw an error. :-) )
You can use Async await for waiting promise to resolve
public handleDropdownChange = async e => {
this.setState({ selectedOption: e.target.value, isLoading: true });
await this.getSecurityGroupUsers();
await this.checkForDocAccess();
await this.getDocuments();
await this.hideMarkAsReviewed();
}
You need a function call back!
function functionOne(x) { return x; };
function functionTwo(var1) {
// some code
}
functionTwo(functionOne);
Use async await and a simple sleep function to wait between them if it is required
const sleep = time => new Promise(res => {
setTimeout(res, time);
});
async handleDropdownChange(e) {
// Updates dropdown select
this.setState({ selectedOption: e.target.value, isLoading: true });
// Checks security permissions - MAKES A GET REQUEST
await sleep(1000);
await this.getSecurityGroupUsers();
await sleep(1000);
// Ghecks if user can access docs
await this.checkForDocAccess();
await sleep(1000);
// Gets documents - MAKES A GET REQUEST
await this.getDocuments();
await sleep(1000);
// Delete Mark as Reviewed property THEN displays docs
await this.hideMarkAsReviewed();
}
// But you methods should return a promise like this
getDocuments = () => {
return fetch('some url').then(res => res.json());
}
// Or something like
getSecurityGroupUsers = async () => {
const data = await axios('some url');
return data.message;
}

Does this.setState return promise in react

I made my componentWillMount() async. Now I can using await with the setState.
Here is the sample code:
componentWillMount = async() => {
const { fetchRooms } = this.props
await this.setState({ })
fetchRooms()
}
So question here is this.setState returns promise because I can use await with it?
Edit
When I put await then it runs in a sequence 1, 2, 3 And when I remove await then it runs 1, 3, 2??
componentWillMount = async() => {
const { fetchRooms } = this.props
console.log(1)
await this.setState({ } => {
console.log(2)
})
console.log(3)
fetchRooms()
}
You can promisify this.setState so that you can use the React API as a promise. This is how I got it to work:
class LyricsGrid extends Component {
setAsyncState = (newState) =>
new Promise((resolve) => this.setState(newState, resolve));
Later, I call this.setAsyncState using the standard Promise API:
this.setAsyncState({ lyricsCorpus, matrix, count })
.then(foo1)
.then(foo2)
.catch(err => console.error(err))
setState is usually not used with promises because there's rarely such need. If the method that is called after state update (fetchRooms) relies on updated state (roomId), it could access it in another way, e.g. as a parameter.
setState uses callbacks and doesn't return a promise. Since this is rarely needed, creating a promise that is not used would result in overhead.
In order to return a promise, setState can be promisified, as suggested in this answer.
Posted code works with await because it's a hack. await ... is syntactic sugar for Promise.resolve(...).then(...). await produces one-tick delay that allows to evaluate next line after state update was completed, this allows to evaluate the code in intended order. This is same as:
this.setState({ roomId: room && room.roomId ? room.roomId : 0 }, () => {
console.log(2)
})
setTimeout(() => {
console.log(3)
});
There's no guarantee that the order will stay same under different conditions. Also, first setState callback isn't a proper place to check whether a state was updated, this is what second callback is for.
setState does not return a promise.
setState has a callback.
this.setState({
...this.state,
key: value,
}, () => {
//finished
});
It does not return a promise.
You can slap the await keyword in front of any expression. It has no effect if that expression doesn't evaluate to a promise.
setState accepts a callback.
Don't think setState is returning a Promise but you can always do this
await new Promise( ( resolve ) =>
this.setState( {
data:null,
}, resolve )
)
or you can make some utility function like this
const setStateAsync = ( obj, state ) => {
return new Promise( ( resolve ) =>
obj.setState( state , resolve )
)
}
and use it inside a React.Component like this:
await setStateAsync(this,{some:'any'})
You can simple customize a Promise for setState
componentWillMount = async () => {
console.log(1);
await this.setRooms();
console.log(3);
};
setRooms = () => {
const { fetchRooms } = this.props;
return fetchRooms().then(({ room }) => {
this.setState({ roomId: room && room.roomId ? room.roomId : 0 }, _ =>
console.log(2)
);
});
};
Or
setRooms = async () => {
const { fetchRooms } = this.props;
const { room } = await fetchRooms();
return new Promise(resolve => {
this.setState({ roomId: room && room.roomId ? room.roomId : 0 }, _ =>
resolve()
);
});
};
Hope this help =D
Simpler way to answer this question is we can use promisify function present in pre-installed util library of node.js and then use it with the await keyword.
import {promisify} from 'util';
updateState = promisify(this.setState);
await this.updateState({ image: data });

React component async await in other funtion

Quick question about async await, there are a lot of examples to use async await with React, but I have trouble to get it working.
componentDidMount = () => {
const cards = mockCards;
this.setState(
{
loading: false,
cointrackerCards: [
...cards.map(
card => {
const price = await this.getCurrentSellPrice(card.coin, card.valuta, card.amount[card.coin])
return {
...card,
currentSellPrice: price
}
}
)
]
}
)
}
getCurrentSellPrice = async (coinType, valuta, amount) => {
//console.log(coinType, valuta, amount)
try {
const result = await coinbaseAPI.get('/prices/BCH-EUR/sell')
//console.log(result.data.data)
return result.data.data.amount
}
catch (err) {
console.log('[ERRORHANDLER]', err)
}
}
The above code throws a error: Syntax error: await is a reserved word (71:42) Directly calling the function in the currentSellPrice key, does not work either, as it returns a Promise. What am I doing wrong?
Your problem: you can't awaiting something without async scope, this is what you do in/with componentDidMount. If you would like to use await inside componentDidMount mark it as async. Here is a working example of how it works:
class AsyncState extends React.Component {
state = {
flag: false
}
async componentDidMount(){
const flag = await this.changeFlagAsync();
this.setState({flag})
}
async changeFlagAsync(){
return new Promise((resolve, reject) => { // simulate async
setTimeout(() => resolve(true), 2000)
})
}
render() {
return <div>
{this.state.flag && <div>Done after 2 sec</div> || <div>Waiting for promise</div>}
</div>;
}
}
Also working fiddle
You are making two mistakes first is you didn't assign async keyword to the function. at cards.map.
anyways I guess it won't work even if you write it there because async await won't work in map use for in, for of or traditional for loop for doing this.
refer to this answer Using async/await with a forEach loop

Categories