Does this.setState return promise in react - javascript

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 });

Related

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

Wait for function to setState before triggering next function

async componentDidMount() {
this.loadSelectors();
this.useSelectors();
};
loadSelectors = () => {
this.setState({"Selector": "Test"});
}
useSelectors = () => {
console.log(this.state.Selector);
}
How do I wait for loadSelectors() to finish setting the state before I call useSelectors()? I know the normal way to do this is
this.setState({"Selector": "Test"}, => this.useSelectors());
but my case seems different, I've tried
async componentDidMount() {
await this.loadSelectors(() => useSelectors());
}
and It does not work.
It seems you want to promisify this.setState, but loadSelectors does not return anything. It should return a promise that resolves when the callback -- that should be passed to setState -- is called:
var loadSelectors = () => new Promise(resolve =>
this.setState({"Selector": "Test"}, resolve)
);
One rule to follow when you use await: if the goal is to await a certain future event, then the expression that follows it should be a promise, as otherwise there is not going to be much awaiting.
I managed to figure out what I needed, just pass the useSelectors function as a callback function and use it in loadSelectors
componentDidMount() {
this.loadSelectors(() => this.useSelectors());
};
loadSelectors = (callbackFunction) => {
this.setState({"Selector": "Test"}, callbackFunction);
}
useSelectors = () => {
console.log(this.state.Selector);
}

React setState callback return value

I'm new in React and I was looking to achieve this kind of flow:
// set the state
// execute a function `f` (an async one, which returns a promise)
// set the state again
// return the promise value from the previous function
So, what I'm doing now is the following:
async function handleSomething() {
this.setState((prevState) => { ... },
() => {
let result = await f()
this.setState((prevState) => { ... },
...
)
})
return result;
}
Hope you get the idea of what I want to achieve. Basically I want to get result, which is the value returned from awaiting f, and return it in handleSomething so I can use it in another place, but wrapping it up inside those setState calls:
// g()
// setState
// res = f()
// setState
// return res
My question is, how can I do this properly? Maybe should I modify the state with the result value and get it from there?.
EDIT:
Usage of handleSomething:
// inside some async function
let result = await handleSomething()
You can create a Promise that resolves once both setState calls are done:
function handleSomething() {
return new Promise(resolve => {
this.setState(
prevState => {
/*...*/
},
async () => {
let result = await f();
this.setState(
prevState => {
/*...*/
},
() => resolve(result)
// ^^^^^^^ resolve the promise with the final result
);
}
);
});
}
Which would be used like:
this.handleSomething().then(result => /* ... */)
// or
const result = await this.handleSomething();

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

Change loading state after function call

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

Categories