new Promise with set state receives an arrow function - javascript

I was going through a react code base where I see the Promise function calling setstate with 2 arguments, I cannot exactly decipher how exactly this is working, everywhere I have seen a setState being created having one object argumemnt, but here we have a resolve argument that gets a setstate with an object and an arrow function which is calling resolve. The other arrow function, which I don't have any idea what it's doing and how this code is working
this is what I have for state
constructor(props){
super(props);
this.state = {
articles:[],
loading:true,
page:1,
totalResults : 0
}
}
This is the code that I have in my codebase which I have trouble understanding.
handleNextClick = async ()=>{
await new Promise(resolve => this.setState({page: this.state.page+1}, () => resolve())); // same as await this.setState({page: this.state.page+1});
await this.updateNews();
}
As you can see Promise is receiving theresolve arrow function which is calling setstate with an object and an arrow function. Can someone please explain how this is working exactly.

The code doesn't make a lot sense which is probably why it's difficult to understand.
setState has a callback option. So you don't need a promise, and you don't need to await the setState either. Further, you probably don't need to await updateNews either so you can remove the async from the handleNextClick function.
This is likely what the function should look like.
handleNextClick () {
// Set the new state, then use the callback option
// to call `updateNews`
this.setState( { page: this.state.page + 1 }, () => {
this.updateNews();
});
}

Related

Setting a state but still getting error when trying to use after it

I'm trying to get pinned article
const getCurrentlyPinned = async() =>{
setLoader(true)
await firestore()
.collection('admin_control')
.doc('currently_Pinned')
.get()
.then(snapshot =>{
const data = snapshot.data();
setpinnedNewsID(data.pinnedNewsId)
})
}
useEffect(() => {
getCurrentlyPinned().then(()=>{
console.log(pinnedNewsID)
})
}, [])
therefore calling it from useEffect and console logging it in .then function, but I'm getting its value as undefined. I dont know why I'm getting this.
That's a very common stuff, I guess there must be some similar question over stackoverflow but let me answer it for you. States are asynchronous so it takes a bit time of course to set it.
Just do the following stuff.
useEffect(() => {
getCurrentlyPinned()
}, [])
useEffect(() => {
console.log(pinnedNewsID)
}, [pinnedNewsID])
the state which you're setting in firebase function, just pass it in the dependency array of another useEffect so that you will console.log only when its value has been changed(or set)
async functions take some time to return data.
useEffect trying to access the data before it's ready. bc it runs immediately after the component renders. that's why pinnedNewsID value is Undefined.
do this instead: use 2 useEffect hooks.
useEffect(() => { getCurrentlyPinned() }, [])
useEffect(() => { console.log(pinnedNewsID) }, [pinnedNewsID])

nodejs - async.each function with async operation on each iteration

I have a question about async.each behavior.
consider the code:
const examples = [1,2];
const asyncTask = (index) => {
return new Promise(resolve => {
setTimeout(() => {
console.log(`Inside setTimeout-${index}`);
resolve(true);
}, 1500)
});
}
function testingAsyncEach(callback){
async.each(examples, (example, innerCallback) => {
asyncTask(example).then(isDone => {
console.log(`isDone: ${isDone}`);
innerCallback(null);
});
}, err => {
console.log('done testingAsyncEach');
return callback()
})
}
testingAsyncEach(() => {console.log('final callback testingAsyncEach');})
a simple code using the "async" module in nodejs, using the array [1,2] and on each item in the array executing the function "asyncTask" which returns a new promise which gets resolve after a timeout of 1.5 seconds.
in this scenario the output of the program is:
Inside setTimeout-1
isDone: true
Inside setTimeout-2
isDone: true
done testingAsyncEach
final callback testingAsyncEach
but when I changed the "testingAsyncEach" function to use the "await" syntax:
function testingAsyncEach(callback){
async.each(examples, async (example, innerCallback) => {
const isDone = await asyncTask(example);
console.log(`isDone: ${isDone}`);
innerCallback(null);
}, err => {
console.log('done testingAsyncEach');
return callback()
})
}
the async.each is not waiting for the "asyncTask" to end. output:
Inside setTimeout-1
isDone: true
done testingAsyncEach
final callback testingAsyncEach
Inside setTimeout-2
isDone: true
Can you please help me understand why using the "await" instead of the "then" change the behavior? how can I still use the "await" syntax and get the proper output?
Thanks!
According to the documentation of the async module, if you pass an async function, it won't pass the innerCallback to your function. I believe that your innerCallback(null) call therefore errored, making the async.each return early. (the 2nd example is already "launched" though, so that still happens afterwards)
Check if err is set to an error, and if so, log it.
Should that be the issue, removing the innerCallback argument and call should solve it.

setState inside a Promise function in a useEffect with hooks?

I'm trying to set the value of a hook inside a Promise function inside a useEffect() and being able to store the returned promise value in the fruit hook so I can access it in the return function of MyComponent()
This is what I tried so far:
const MyComponent = () => {
const [fruit, setFruit] = useState('')
useEffect(() => {
// my promise function that returns a string (a random fruit)
promiseFunction()
.then(value => setFruit(value))
}, [])
return <div>fruit ? fruit : Loading...</div>
}
I have tried many approachs so far but none of them worked.
useEffect(() => {
promiseFunction()
.then(value => setFruit(() => value))
}, [])
useEffect(() => {
promiseFunction()
.then(value => setFruit((value) => { fruit = value }))
}, [])
After all that, the returned value of the hook is still empty.
There are no errors messages or anything, I tried debugging using another useEffect and it still returns an in the console.
How can I get the returned value of the promise function and store it in the fruit variable?
The first code example looks perfect to me. My only concern is that the value is not being correctly returned from the function itself.
Have you tried to log the return from the promiseFunction? Another way to write this code would be to use a function that you can call inside the useEffect itself, like this:
useEffect(() => {
async function loadFruitData() {
const promiseFunctionReturn = await promiseFunction()
console.log(promiseFunctionReturn)
setFruit(promiseFunctionReturn)
}
//Call the function
loadFruitData();
}, [])
This should give you a better way to see whats happening, and I also find it more readable.
But remember, if you're trying to console.log() the data right after the setFruit, it wouldn't show you the updated state, it happens because React has a queue that make the updates caused by the useState, that make it looks like an asynchronous update.

Getting an error after using setState with a promise

Code:
onDragEnd = {
(event) => this.setState({ playerMarkerPositionFuture: event.nativeEvent.coordinate })
.then(() => alert("hello"))
}
When the following code gets executed I receive this error:
undefined is not an object evaluating
('_this.setState({playerMarkerPositionFuture: event.nativeEvent.coordinate}).then')
If I remove the promise everything works as expected so it means that most likely I used the promise in a wrong way:
onDragEnd={
(event) => this.setState({ playerMarkerPositionFuture: event.nativeEvent.coordinate })
}
setState accepts a callback, but it doesn't return a promise. See docs
Calls to setState are asynchronous - don’t rely on this.state to reflect the new value immediately after calling setState. Pass an updater function instead of an object if you need to compute values based on the current state (see below for details).
(event) => {
this.setState({playerMarkerPositionFuture: event.nativeEvent.coordinate }, () => alert("hello"));
}
setState doesn't return a Promise. It is most probably a void function.
Instead of using a promise to call alert after setState is finished, use a callback instead
onDragEnd={(event) =>
this.setState({
playerMarkerPositionFuture: event.nativeEvent.coordinate
}, () => {
alert("hello")
})}
onDragEnd={(event) => this.setState({ playerMarkerPositionFuture: event.nativeEvent.coordinate }).then(() => alert("hello"))}
This is a wrong practise of using setState() method. You can only use a callback using setState().
Right Practise:
onDragEnd={(event) => this.setState({ playerMarkerPositionFuture: event.nativeEvent.coordinate },()=>{
alert("hello")
})
Have a look at this article:
https://medium.com/#voonminghann/when-to-use-callback-function-of-setstate-in-react-37fff67e5a6c
setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied.
https://reactjs.org/docs/react-component.html#setstate
so you could use setState(updater, callback) to execute code after the state was altered, to be on the save side
If you want use promise ...another solution could be
setStateAsync(state) {
return new Promise((resolve) => {
this.setState(state, resolve)
});
}
and use that ...
this.setStateAsync(
{playerMarkerPositionFuture: event.nativeEvent.coordinate}
)
.then(() => alert("hello"))
Try this
onDragEnd={(event) => this.setState({ playerMarkerPositionFuture: event.nativeEvent.coordinate },() => alert("hello"))}

React and jest mock module

I am creating an application in which I use redux and node-fetch for remote data fetching.
I want to test the fact that I am well calling the fetch function with a good parameter.
This way, I am using jest.mock and jasmine.createSpy methods :
it('should have called the fetch method with URL constant', () => {
const spy = jasmine.createSpy('nodeFetch');
spy.and.callFake(() => new Promise(resolve => resolve('null')));
const mock = jest.mock('node-fetch', spy);
const slug = 'slug';
actionHandler[FETCH_REMOTE](slug);
expect(spy).toHaveBeenCalledWith(Constants.URL + slug);
});
Here's the function that I m trying to test :
[FETCH_REMOTE]: slug => {
return async dispatch => {
dispatch(loading());
console.log(fetch()); // Displays the default fetch promise result
await fetch(Constants.URL + slug);
addLocal();
};
}
AS you can see, I am trying to log the console.log(fetch()) behavior, and I am having the default promise to resolve given by node-fetch, and not the that I've mock with Jest and spied with jasmine.
Do you have an idea what it doesn't work ?
EDIT : My test displayed me an error like my spy has never been called
Your action-handler is actually a action handler factory. In actionHandler[FETCH_REMOTE], you are creating a new function. The returned function taskes dispatch as a parameter and invokes the code you are showing.
This means that your test code will never call any function on the spy, as the created function is never invoked.
I think you will need to create a mock dispatch function and do something like this:
let dispatchMock = jest.fn(); // create a mock function
actionHandler[FETCH_REMOTE](slug)(dispatchMock);
EDIT:
To me, your actionHandler looks more like an actionCreator, as it is usually called in redux terms, though I personally prefer to call them actionFactories because that is what they are: Factories that create actions.
As you are using thunks(?) your actionCreater (which is misleadingly named actionHandler) does not directly create an action but another function which is invoked as soon as the action is dispatched. For comparison, a regular actionCreator looks like this:
updateFilter: (filter) => ({type: actionNames.UPDATE_FILTER, payload: {filter: filter}}),
A actionHandler on the other hand reacts to actions being dispatched and evaluates their payload.
Here is what I would do in your case:
Create a new object called actionFactories like this:
const actionFactories = {
fetchRemote(slug): (slug) => {
return async dispatch => {
dispatch(loading());
console.log(fetch()); // Displays the default fetch promise result
let response = await fetch(Constants.URL + slug);
var responseAction;
if (/* determine success of response */) {
responseAction = actionFactories.fetchSuccessful(response);
} else {
responseAction = actionFactories.fetchFailed();
}
dispatch(responseAction);
};
}
fetchFailed(): () => ({type: FETCH_FAILED, }),
fetchSuccessful(response): () => ({type: FETCH_FAILED, payload: response })
};
Create an actionHandler for FETCH_FAILED and FETCH_SUCCESSFUL to update the store based on the response.
BTW: Your console.log statement does not make much sense too me, since fetch just returns a promise.

Categories