How to wait for a dispatch to end before continuing runtime - react-native

I'm building an app using React Native, and I have the following code
sumbit_answer(answer) {
this.setState({isLoading: true})
if (this.props.curr_round_correct_answer == '') {
this.props.dispatch(insert_correct_answer(answer));
//replace
} else {
this.props.dispatch(insert_answer(this.props.current_player, answer));
}
if (
this.props.curr_round_results.length ==
this.props.players_names.length - 1
) {
this.props.navigation.replace('RoundStatistics');
} else {
this.props.navigation.replace('PassPlayer');
}
}
Basically I want to run the second if statement only when the dispatch from the previous if is completed, that is currently not happening. The dispatch does not make any API calls, only changes to the redux state. So how can I prevent this from happening? I though about using the await keyword, but does not feel right.Thanks!!!

This is a somewhat opinionated answer, but I think the best way to handle this is to move the second section elsewhere and make it conditional based on a combination of local state and selectors. You can call it within a useEffect hook.
You should be able to tell the dispatch has completed and when you are ready for the next step by looking at the redux state. You can also use local state to store boolean flags like didSubmitAnswer if needed. Somehow you combine that information into an isCompletedRound boolean. Your useEffect hook fires the navigation actions if isCompletedRound === true.

Related

React Native console.log old value useState

I'm having trouble with React Native showing wrong value for me. I wan't to show the value after an useState update. My goal is to pass the value to the parent component but right now it passes the opposite value (true when switch is off). What do I have to do to console.log the right value after a useState update?
Watch image for example here
The useState hook is somewhat asynchronous (although you cannot wait for it).
Try using a useEffect:
useEffect(() => {
console.log(isEnabled)
}, [isEnabled]) // Array of dependencies: when any of these value changes, the function in the useEffect will re-run
More information here:
https://dev.to/shareef/react-usestate-hook-is-asynchronous-1hia
https://javascript.plainenglish.io/why-you-shouldnt-always-use-usestate-658994693018
The Change function will always "see" the state value that existed at the time of running the function. This is not because of asynchronicity per se (state updates are actually sync) but because of how closures work. It does feel like it is async though.
The state value will properly update in the background, but it won't be available in the "already-running" function. You can find more info here.
The way I see your handler implemented though:
const handleChange = () => {
setIsEnabled(!isEnabled) // you do not need updater function, you can directly reference the state
triggerParentMethod(!isEnabled); // then you can also directly call the parent function here
}
I recommend this as this way you will notify the parent immediately on user click instead of waiting for the state to be set and then notifying the parent in the next render cycle (in the effect), which should be unnecessary.
State updates in React are asynchronous, meaning that React does not wait for the state to be updated before executing the next line of code. In your case, the state update setIsEnabled(...) is not finished before console.log(isEnabled) is run, and therefore it returns the old value.
Just put the console.log(isEnabled) outside the function for it to print the update correctly. The component SetupSwitch is re-rendered when the state isEnabled is updated, which means it prints the console.log of the updated variable again.
...
console.log(isEnabled);
const Change = () => {
...
You will have to implement useEffect to view the changes.
useState is an asynchronous function it will go to the callback queue, meanwhile, the value will be consumed, so you need to trigger the action whenever the count changes. (for this example)
const [count, setCount] = useState(0);
useEffect(() => console.log(count), [count]);
setCurrPos(preevCount => prevCount + 1);

If I update state in the middle of a function, what will be the execution order for the rest of the function?

I'm using the Context API and React functional components with hooks. I have a functional component ProfileForm.js
At the top of this file I call useContext so I can get access to the current state (an array of profile objects).
const {state: {userProfiles}, addProfile, removeProfile, editProfile} = useContext(UserProfilesContext);
For this example I will focus on the function addProfile. When the user clicks the submit button, I want to add the new profile to the global state/context and I want to save the updated list to AsyncStorage.
Here is my handler:
const saveProfileHandler = async(profiles) = >
{
const {
firstName, lastName, userPhone, userEmail, userMonth, userDay, userYear,
userStreet, userCity, userState, userZip,
}
= formState.inputValues;
Keyboard.dismiss();
if (validateForm()) {
let month = userMonth;
let day = userDay;
if (month.length == = 1) {
month = `0 $ { userMonth }
`;
}
if (day.length == = 1) {
day = `0 $ { userDay }
`;
}
const profile = new UserProfile(
firstName.trim(),
lastName.trim(),
userPhone.trim(),
userEmail.trim(),
month.trim(),
day.trim(),
userYear.trim(),
userStreet.trim(),
userCity.trim(),
userState.trim(),
userZip.trim(), );
// add profile to Context object
addProfile(profile);
await saveUserProfilesToStorage([... profiles, profile ]);
navigation.pop();
}
};
When I call addProfile I update the global state/context, so I know that React will re-render my component. So, I have 2 questions really:
Is it unsafe to rely on the global state value that I just saved. I mean, can I use the updated context state and save that to AsyncStorage or will it not be updated yet and thus unreliable?
After I call addProfile does the rest of the function continue to run before re-rendering from the state update, or does addProfile cause the component to re-render before the rest of the function finishes? If it does re-render in the middle of the function call, when does the rest of the function execute?
Thanks in advance.
This is what I was able to learn. I'll put it up here for others that stumble upon it.
It is important to know that in React, setState() is an asynchronous function. The JavaScript engine is made up of the memory heap and the call stack. The call stack will run all synchronous functions. Along side the JavaScript engine are the Web APIs (provided by the browser) and an Event Loop (callback queue).
When a function is executed it is placed on the call stack and execution begins synchronously. If you call another function from inside the currently running function, the new function will get its own execution context. The current function will pause execution, the new function will execute to completion (assuming no new function calls inside its context) and return control to the first function which will continue execution.
Asynchronous Events
Asynchronous code runs within the Web APIs environment of the browser. This prevents the code from blocking the JavaScript thread/call stack. Callback functions for the asynchronous code are registered in the Web APIs environment. When the callback is ready to be executed it is placed in the callback queue. All callbacks, except those returned by promises, are queued up here.
The callbacks won't be executed until the call stack is empty. Then they will be executed in a FIFO (first in first out) order.
It is also important to know that callbacks from promises (then, catch) don't get put into the callback queue. They go into a micro tasks queue. This queue has priority over the callback queue. This means all of the callbacks from the micro tasks queue will be called before the callback queue's tasks.
In my case, the context update will occur after navigation.pop(); but since the context is global and I'm not updating the UI after the component has unmounted it should be okay.
If I'm wrong on anything I welcome corrections.

Vuejs Watch jumping one tick

I'm developing an app using Vuejs and Vuex.
I've got a Vuex module called settings_operations. This module has the following action:
async changePassword ({ commit }, { password, id }) {
commit(CHANGE_PASSWORD_PROCESSING, { id })
const user = auth.currentUser
const [changePasswordError, changePasswordSuccess] = await to(user.updatePassword(password))
if (changePasswordError) {
commit(CHANGE_PASSWORD_ERROR, { id, error: changePasswordError })
} else {
commit(CHANGE_PASSWORD_SUCCESS, changePasswordSuccess)
}
}
Edit: the to() is https://github.com/scopsy/await-to-js
With the following mutations:
[CHANGE_PASSWORD_PROCESSING] (state, { id }) {
state.push({
id,
status: 'processing'
})
},
[CHANGE_PASSWORD_ERROR] (state, { id, error }) {
state.push({
id,
error,
status: 'error'
})
}
And then, in the component I want to use this state slice:
computed: {
...mapState({
settings_operations: state => state.settings_operations
})
},
watch: {
settings_operations: {
handler (newSettings, oldSettings) {
console.log(newSettings)
},
deep: false
}
}
The problem is that when the changePassword action results in an error, the watch doesn't stop in the PROCESSING step, it goes directly to the ERROR moment so the array will be filled with 2 objects. It literally jumps the "processing" watching step.
A funny thing that happens is that if I add a setTimeout just like this:
async changePassword ({ commit }, { password, id }) {
commit(CHANGE_PASSWORD_PROCESSING, { id })
setTimeout(async () => {
const user = auth.currentUser
const [changePasswordError, changePasswordSuccess] = await to(user.updatePassword(password))
if (changePasswordError) {
commit(CHANGE_PASSWORD_ERROR, { id, error: changePasswordError })
} else {
commit(CHANGE_PASSWORD_SUCCESS, changePasswordSuccess)
}
}, 500)
},
It works! The watch stops two times: the first tick displaying the array with the processing object and the second tick displaying the array with 2 objects; the processing one and the error one.
What am I missing here?
Edit:
I reproduced the problem here: https://codesandbox.io/s/m40jz26npp
This was the response given in Vue forums by a core team member:
Watchers are not run every time the underlying data changes. They are only run once on the next Tick if their watched data changed at least once.
your rejected Promise in the try block is only a microtask, it doesn’t
push execution to the next call stack (on which the watchers would be
run), so the error handling happens before the watchers are run.
additionally, when you mutat an object or array, the newValue and
oldValue in a deep watcher will be the same. See the docs:
Note: when mutating (rather than replacing) an Object or an Array, the old value will be the same as new value because they reference the
same Object/Array. Vue doesn’t keep a copy of the pre-mutate value.
and as a final sidenote, I’ve never seen anyone use an aray as the
root state of a module, I have no idea if that will work for vuex in
all possible circumstances. I certainly would not recommend doing
this.
Edit with a better and more complete answer from the same member:
Why watchers are asynchronous at all? Because in the vast majority of
use cases, watchers only need to react to the last synchrnous change
that was done. In most cases (in the context of a component), it would
be couterproductive to to react to every change since you would
re-trigger the same behaviour mutliple times even though in the end,
only the last state is the important one.
In other words: Running a watcher on each change by default would
probably lead to apps that burn a lot of CPU cycles doing useless
work. So watchers are implemented with an asynchronous queue that is
only flushed on nexTick. And we don’t allow duplicate watchers then
because the older instance of a watcher would apply to data that
doesn’t “exist” anymore in that state once the queue is flushed.
An important note would be that this only applies to synchronous
changes or those done within a microtask, i.e. in an immediatly
resolving or failing promise - it would, for example, not happen with
an ajax request.
Why are they implemented in a way that they are still not run after a
microtask (i.e. an immediatly resolved promise? That’s a bit more
coplicated to explain and requires a bit of history.
Originally, in Vue 2.0, Vue.nextTick was implemented as a microtask
itself, and the watcher queue is flushed on nextTick. That meant that
back then, a watcher watching a piece of data that was changed two
times, with a microtask (like a promise) in between, would indeed run
two times.
Then, around 2.4 I think, we discovered a problem with this
implementation and switched Vue.nextTick to a macroTask instead. under
this behaviour, both data chhanged would happen on the current call
stack’s microtaks queue, while the watcher queue would be flushed at
th beginning of the next call stack, wich means it will only run once.
We found a couple of new problems with this implementation that are
much more common than the original issue with microtasks, so we will
likely switch back to the microtask implementation in 2.6. Ugly, but
necessary.
So, this should do the trick for now:
await Vue.nextTick();

React Native call this.setState inside function

I'm using a xml parser react-native-xml2js in react native, but this "plugin" uses a specific function for parse the xml, I wasn't found a correct way to use "this" within the function, I've tried using bind() in the callback but doesn't work as expected when using bind it fills my variable moments later after executed, so I don't know how to use it, this is my code:
state = { artcicles: null }
componentDidMount() {
fetch('http://example.com/rss.xml')
.then((response) => response.text())
.then((response) => {
parseString(response, function (err, result) {
this.setState({
articles: JSON.stringify(result.rss.channel[0].item)
})
console.log('RAW: ' + result.rss.channel[0].item);
console.log('THIS: ' + this.state.articles);
}.bind(this));
});
}
When calling this.state.articles in render() at beginning shows null but a second later it fills the articles variable but at that moment the app shows the error when I'm trying to access to the variable.
Any ideas?
Thanks.
I can help you observe something. In React, setState is asynchronous, so the code on the following line after setState will be executed immediately after the setState call is placed in the event loop.
Your true issue is that those console.logs are working perfect, you aren't crazy. They are just being executed before setState has completed.
The secret trick here is that setState accepts a second parameter which is a callback that will be executed after the state is updated. You could place all your following logic inside that statement.
Here is a sampler pack:
this.setState({ dogs: 350 }, () => { console.log('The state has been updated.') })
The second parameter to setState() is an optional callback function that will be executed once setState is completed and the component is re-rendered. Generally we recommend using componentDidUpdate() for such logic instead.
Cite: https://reactjs.org/docs/react-component.html
In yours, it could look like this:
componentDidMount() {
fetch('http://example.com/rss.xml')
.then((response) => response.text())
.then((response) => parseString(response, (err, result) =>
this.setState({
articles: JSON.stringify(result.rss.channel[0].item),
}, () => {
console.log('RAW:', result.rss.channel[0].item)
console.log('THIS:', this.state.articles)
}));
);
}
Checkout the modification I did on the console.log. It too can accept multiple parameters.
A list of JavaScript objects to output. The string representations of each of these objects are appended together in the order listed and output. Please be warned that if you log objects in the latest versions of Chrome and Firefox what you get logged on the console is a reference to the object, which is not necessarily the 'value' of the object at the moment in time you call console.log(), but it is the value of the object at the moment you click it open.
Cite: https://developer.mozilla.org/en-US/docs/Web/API/Console/log
I like that definition additionally because it speaks to the asynchronous nature of live references. One function by itself can be synchronous, but due to the callstack and function queue, you can load up an infinite number of functions into the queue and they will complete in a random order based on how long each one takes to complete because only one passes through the callstack at a time, on the main thread. Of course, it seems random order to us, but it's actually the mathematically exact fastest path through all those functions, assuming they are all deterministic.
Fast forward to the code in your question, setState doesn't care to stop surrounding code from executing unless you explicitly tell it to. That's what the callback is for, if you need to run some bonus code.
While we are talking about setState, I should mention also that you can pass a function to it. Imagine that the second parameter callback is your method of looking into the future after setState. The opposite of that is looking into the past, which is where the functional setState comes in handy by giving you closure around the previous unit of time. The previous state also happens to be the current state of whatever you are updating.
Here is a sampler pack for that:
this.setState((prevState) => {
// hello I like cats.gif
// we could run some fascinating code here
// as long as we return an object
console.log('rad')
return {
articles: [ ...prevState.articles, { new: 1, article: true }],
}
})
It gives you a safe window to guarantee state integrity through your update. I showed that example there as spreading an Array into a new Array and appending it with an object to demonstrate similar to a real scenario you might need to refer to the current state as part of your operations.
In a real scenario, you might sharpen that up to this, which capitalizes on implicit return of an Object literal (requires fat arrow syntax):
this.setState(prevState => ({
articles: [ ...prevState.articles, { new: 1, article: true }],
})
Hopefully that helps us see the climate of what is happening. In React, it is important to undergo a formal change management process, so every time you are getting or setting data, you need to be careful who is reading or writing data and from where, like which functions and which part of the program. React's way of taming JavaScript is to try to force data to always flow unidirectionally, immutably, and deterministic.
It makes things easier to reason about if everything is flowing one way. That only works if you require immutability and prefer a deterministic system. It means most functions are written declaratively, so they declare what the state looks like at the start of a function, do stuff, then declare what the state is at the end of the function.
React makes you think you are writing mostly pure JavaScript, but really it is managing your state using a first in, first out technique to avoid race conditions when perhaps thousands of components are trying to write to the state at the same time. While the user is in the browser rolling their face across the keyboard triggering all kinds of events, and we must not block the main thread or else suffer poor UX.
A formal change management process means there is probably an official pattern that you should use every time you get or set data. Luckily, the patterns are usually what you would do if you were writing pure JavaScript. Reactive programming and immutability help tame the wild asynchronous concurrency gods.
Sorry, we are digressing a bit, but I had to do it for science.
TLDR,
it's very important what you are doing before, during, and after this.setState(). It's a special function, a class method on the Component Class. I hope I have helped us understand a couple of its secrets today.
You were seeking to perform two operations in one setState call. Normally, you only do one which is to set the state :) Your intended usage is fine. We do nest one additional dimension, but it's fine because you are just performing one more operation. I wouldn't recommend it if you were doing a chain of functions in the callback.
Notice the React documentation that states,
Generally we recommend using componentDidUpdate() for such logic instead.
The reason it says that is componentDidUpdate() is listening for state changes, so you can run logic there that is listening for certain conditions and then acting. It saves you from having to care about performing a second operation after setState at the callsite.
Imagine you did this.state.hasFetchedStuff = true inside your componentDidMount() and then had something like this in componentDidUpdate():
componentDidUpdate() {
if (this.state.hasFetchedStuff) {
this.triggerSomething()
}
}
That can free your componentDidMount from having to care about anything after getting the data, which is perhaps good decoupling and a nice separation of concerns.
I converted this post into a Medium article as well and added much more detail: https://medium.com/#agm1984/reacts-setstate-is-a-special-function-and-it-helps-with-asynchronous-concurrency-669eddbe3dd1
In the render() method you can use an inline If:
{this.state.articles && (
// Display articles
)}
When articles stops being null the element right after && will appear and while it's null, no errors will be thrown.
Instead of using null for the initial value you can use an empty array []. That way your articles state variable is never in a consistent state. Also, you can avoid having a manual bind in your callback and use arrow functions instead which will keep the current scope. This way you'll have the correct closure to use this.state.
parseString(response, (err, result) => {
this.setState({
articles: JSON.stringify(result.rss.channel[0].item)
})
});
This answer to some tricky
componentDidMount() {
fetch('http://example.com/rss.xml')
.then((response) => response.text())
.then((response) => {
parseString(response, function (err, result) {
this.setState({
articles: JSON.stringify(result.rss.channel[0].item)
})
console.log('RAW: ' + result.rss.channel[0].item);
setTimeout(() => {
console.log('THIS: ' + this.state.articles);
}, 1000);
}.bind(this));
});
}

Reflux setState with a Callback

EDIT AGAIN: Opened an issue with Reflux here: https://github.com/reflux/refluxjs/issues/544
EDIT: Reflux setState does not provide any callback for setState. They require you to use the component lifecycle methods to ensure the state is set prior to running any code. If you ever need to use the reflux setState outside of a component, where you do not have lifecycle methods, you will not be guaranteed the state is set. This is due to how Reflux does their setState. It loops all listening components and calls those components' setState methods. If Reflux were refactored to wait until all the listening components' setState calls complete then call a callback passed into its own setState method, that may work, but it would likely require a large rework of Reflux. I have started using a singleton class to manage some of these variables, as they are fully outside the component lifecycle.
Can you use setState with a callback in ReactNative or is that only in React? I'm using the below syntax and the first debugger is hit, but the second debugger and console log never get hit.
EDIT: After digging some more, it seems this does not occur when using setting the state directly, but only when running it through a reflux store and/or not using a component.
See snack here: https://snack.expo.io/S1dm3eFoM
debugger
this.setState(
params,
() => {
debugger
console.log("CALLIN IT BACK")
}
)
I'm the creator of Reflux's ES6 styled stores/component hookups. Hopefully I can shed some light on this for you.
Here's the important points:
1) Reflux sets its store state immediately upon setState calls.
Reflux's store state doesn't have the same problems as React and doesn't need React's workaround (callback). You are guaranteed that your change is immediately reflected in the store's state, that's why there is not a callback. The very next line of code will reflect the store's new state.
tl;dr, no workaround is required.
// in Reflux stores this works
this.setState({foo:'foo'});
console.log(this.state.foo === 'foo') // true
this.setState({foo:'bar'});
console.log(this.state.foo === 'bar') // true
2) Stores can never depend upon components!
The idea that the setState would give a callback about when the dependent components have all updated their state is a major violation of the single most fundamental of all flux principles: 1 way data flow.
If your store requires knowledge about whether or not components are doing something then you are already doing it wrong, and all the problems you are experiencing are XY problems of fundamentally not following flux in the first place. 1-way data flow is a main flux principle.
And that principle exists for good reason. Flux doesn't require 1:1 mapping of store state properties to component state properties. You can map anything to anything, or even just use the store's state for the building blocks of how you will run your own logic to create completely new state properties on the components. For example having loaded and transitioned as separate properties in store state, but mapping to a loadedAndTransitioned property in one component, and a notLoadedOrTransitioned in another component via your own custom logic. That's a hugely powerful part of flux. But your suggestion would pretty much destroy all that, since Reflux can't map people's custom logic.
1-way data flow must be maintained; Store's must operate the same independently of what components utilize them. Without this, the power of flux falls apart!
Store's listen to actions, components listen to stores, actions are called from wherever. All flux-based data flows from action -> store -> component only.
I've checked the library for the refluxjs and the problem and the workaround are as mentioned below.
Problem
The library provides with a new instance of the setState which is not exactly similar to ReactJS setState, which omits the callback as mentioned in their code below.
/dist/reflux.js
proto.setState = function (obj) {
// Object.assign(this.state, obj); // later turn this to Object.assign and remove loop once support is good enough
for (var key in obj) {
this.state[key] = obj[key];
}
// if there's an id (i.e. it's being tracked by the global state) then make sure to update the global state
if (this.id) {
Reflux.GlobalState[this.id] = this.state;
}
// trigger, because any component it's attached to is listening and will merge the store state into its own on a store trigger
this.trigger(obj);
};
Also as mentioned here in the docs
That store will store its state on a this.state property, and mutate its state via this.setState() in a way that is extremely similar to React classes themselves.
WorkAround
The library provides with the listener functions, which provide us with the callbacks of the setState obj of the ReactJS as mentioned in the below snippet.
/dist/reflux.js
componentDidMount: function() {
var me = this;
_.extend(me, ListenerMethods);
this.listenTo(listenable, function(v) {
me.setState(_.object([key],[v]));
});
},
You can use them in the following way
this.listenTo(action, callback)
Hope it clears the doubts
Edit:
Usage as per the docs
To listen inside of the store
constructor()
{
super();
this.state = {count: 0};
this.listenTo(increment, this.incrementItUp);
}
incrementItUp()
{
var newCount = this.state.count + 1;
this.setState({count: newCount});
}
To listen outside of the store anywhere
// listen directly to an action
myActions.actionName.listen(myCallbackFunc);
// listen to a child action
myActions.load.completed.listen(myCallbackFunc);
Here's the link to the snack with working callbacks based on Promises