React Native call this.setState inside function - react-native

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

Related

Can anyone tell how to speed up my vuejs app?

I am a noob in vuejs. This piece of my code is making my app very slow.
<div v-for="(attribute, i) in attributes" :key="i">
<div>{{ AttributeClicked(attribute) }}</div>
</div>
This is the function:
AttributeClicked(attribute) {
this.$store.commit("entities/Attribute/select", attribute.id);
}
This is the mutation:
mutations: {
select(state, id) {
let selection = Attribute.find(id);
Attribute.update({
where: (a) => a.selected,
data: {
selected: false
}
});
if (selection !== null) {
Attribute.update({
where: id,
data: {
selected: true
}
})
}
},
}
The purpose of this code is to make a webpage like this one https://www.tesla.com/models/design#overview
My objective is for example to show the 5 options below Paint attribute when the page loads.
Can anyone tell me how to speed up this app?
You might need to provide more code or info to get to what you want to be doing, but with the code provided I can see several issues. Maybe understanding the problems will help you get to the solution you are looking for.
You've got a function inside the template, these are fine to pass to event handles such as #click, but they can have a negative effect on performance. Whenever you have the template re-render (which happens when certain data changes) it will re-run the functions. In this case you run the function as many times as you have attributes, and if any if the AttributeClicked method causes a reactivity update to propagate to this template, you will have an endless loop.
looks like you're calling a mutation when an action may be more appropriate. Mutations are strictly for updating state in a synchronous manner. The select mutation does not mutate the state, so it's simply wrong to put it in there. Even though it may work, vuex is a tool that not only stores global state, but also organizes it in an opinionated way. If you're going to go against the intended design, you may find it easier to just avoid using it.
I suspect you may be able to execute AttributeClicked(attribute) one time during component mount.

Prevent $emit from emitting more than once in Vue

I have an emit call within my Vue project to update search results, but the emit is being called at least 4 times, because the emit call is defined at various spots, so the data is sometimtes emitted with it and at other spots it is not.
I am using a global bus to perform an emit.
this.$root.bus.$emit('resetValuesInInfiniteLoader', filteredEntities);
this.$root.bus.$on('resetValuesInInfiniteLoader', function (filteredEntities) {});
I tried to name the emits calls differently, and also tried to use a different global vue bus but both options did not really work well.
Any idea how I can do this in an efficient manner so that the $emit is always only called once? How do I need to set it up to ensure that the emit is always only called once? I also tried using $once, which did not work, or tried to destroy the $emit. Can someone give me a small fiddle example or so maybe so I understand how to do this in the right way?
I have found this to be the case also and feel that there are some problems with using it in multiple locations. My understanding is that global event busses are not recommended in most applications as they can lead to a confusing tangle of events. The recommendation is that you use a state management solution like vuex.
But anyway, just a couple of points with your code above. I don't know how you created your bus but I have known to create it as such:
//main.js
const EventBus = new Vue()
Object.defineProperties(Vue.prototype, {
$bus: {
get: function () {
return EventBus
}
}
})
This creates it and makes it global. It can then be triggered in a component or components with:
<button #click="$bus.$emit('my-event')">click</button>
or
methods: {
triggerMyEvent () {
this.$bus.$emit('my-event', { ... pass some event data ... })
}
}
and listened to:
created () {
this.$bus.$on('my-event', ($event) => {
console.log('My event has been triggered', $event)
this.eventItem = 'Event has now been triggered'
//this.$bus.$off('my-event')
})
},
I have found that it works sometimes. I don't know why but it will work then it will trigger several events and I think it is because it isn't finalised or something. You may note I have commented out this.$bus.off which certainly stops it but it then doesn't work again. So I don't know what that's all about.
So there you go, a total non-answer, as in, Yes I've had that too, No I cant fix it.
I went with using vuex store, it seems a lot easier to communicate with any component within the application, has the advantage of global communication, yet does not have the caveat of sending multiple actions such as emit events

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

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

Sending static props to component via selector, best practice

I sometimes have need to send static props to a component, but the data actually comes from my Redux store. I.e. I need a access to state to fetch the data.
With static, I mean that this data won't change during the life of the component, so I don't want to select it from the store on each render.
This is how I solved it at first (the mapStateToProps part):
(state, ownProps) => ({
journalItemType: selectJournalItemType(state, ownProps.journalItemTypeId)
})
The component gets a JournalItemTypeId and the mapStateToProps looks it up in the store and sends the journalItemType to the component. JournalItemType is static metadata and won't change very often, and certainly not during the life of the component.
static propTypes = {
journalItemType: ImmutablePropTypes.map.isRequired,
}
The problem with this is that I call the selector at each render. Not a big performance hit, but feels wrong anyway.
So, I changed to this:
(state, ownProps) => ({
getJournalItemType: () => selectJournalItemType(state, ownProps.journalItemTypeId)
})
The first thing I do in the components constructor is to call getJournalItemType and store the result in the local state. This way the selector is only called once.
static propTypes = {
getJournalItemType: PropTypes.func.isRequired,
}
constructor(props) {
super(props);
this.state = {
journalItemType: props.getJournalItemType()
}
}
Question:
Is this the right way to do this?
Another way would be to let the component know about state so the component could call the selector itself. But I think it's cleaner to keep the state out of the component.
I could also call the selector and fetch the static data earlier in the call chain, but I don't have state naturally available there either.
Clarification:
Why would I store JournalItemTypes in the Redux store if it is static data? All of the apps metadata is in my redux store so it can be easily refreshed from the server. By keeping it in Redux I can treat metadata in the same way as all other data in my synchronisation sagas.
Added clarification after Mika's answer
I need to use the local state because the component is a quite complex input form with all sorts of inputs (input fields, camera, qr-reader, live updated SVG sketch based on input).
A JournalItem in my app is "all or nothing". I.e. if every required field is filled in the user is allowed to save the item. My store is persisted to disk, so I don't want to hit the store more often than needed. So the JournalItem-object (actually an Immutable.map) lives in state until it's ready to be saved.
My selectors are memoized with reselect. This makes my first solution even less impacting on performance. But it still feels wrong.
The component gets updated via props due to other events, so it's re-rendered now and then.
You have a few different options here:
Option 1: the original way
This is the most basic and most 'Redux' way of doing it. If your selectJournalItemType function is moderately light, your app won't suffer much of a performance hit as mapStateToProps is only called when the store is updated according to react-redux docs.
Option 2: the constructor
It is generally recommended to avoid using the Component's state with Redux. Sometimes it is necessary (for example forms with inputs) but in this case it can, and in my opinion should, be avoided.
Option 3: optimizing option 1
If your function is computationally expensive, there are at least a few ways to optimize the original solution.
In my opinion one of the simpler ones is optimizing the react-redux connect. Short example:
const options = {
pure: true, // True by default
areStatesEqual: (prev, next) => {
// You could do some meaningful comparison between the prev and next states
return false;
}
};
export default ContainerComponent = connect(
mapStateToProps,
mapDispatchToProps,
mergeProps,
options
)(PresentationalComponent);
Another possibility is to create a memoized function using Reselect