So my question is somewhat related to how state works in React i.e it gets updated asynchronously. So if we tried to access some state like the following -
this.setState({someState});
console.log('Value:',this.state.someState);
Then the value that will be logged will not be the value that we had just set. So if someone wanted to access the updated value. Then they would need to pass a callback to the setState function. Something like the following -
this.setState({someState}, () => console.log('Value:',this.state.someState));
My question is, in Vue JS, if I try to access the state right after setting it, the right value (i.e the updated value) gets logged. So is it safe to do so? I know we can define watchers in order to perform side effects on state changes. I'm just interested in knowing if the below code is safe to use. I'm also aware that state changes are not rendered right away, and they get updated asynchronously. I just want to know if we can access it, just like in the case of logging it right after setting it to a new value.
this.someState = 'new value'
console.log('Value:', this.someState) //This will log 'new value'
Related
i am having a problem trying to update some template properties if a Vuex store value changes
when i'm setting the value to undefined inside my store action (for example commit('SET_SELECTED_TICKET_ATTACHMENTS', undefined);), everything seems to work fine.
When setting the value to null however (commit('SET_SELECTED_TICKET_ATTACHMENTS', null);, my watch function will not fire.
The watch function in question looks like this:
selectedTicketAttachments () {
this.isTicketAttachmentLoading = false;
}
The mutation looks like this
SET_SELECTED_TICKET_ATTACHMENTS(state, selectedTicketAttachments){
state.selectedTicketAttachments = selectedTicketAttachments;
},
Any help would me much appreciated!
as EstusFlask has already mentioned, the commit will not be executed if the state will not be changed. My problem was that, under certain conditions, null will already have been commited at a time at which the execution of my watch handler would have gone unnoticed.
Update: Here is an example Pen
https://codepen.io/anon/pen/vwzGYY?editors=0011
Preface
Based on my research, it seems like I need a completely different approach. Maybe you can suggest one?
Context
I'm using a Redux-Form (technically an older version, but the API's
in question seem really stable. We can burn that bridge when we get there.) to set some "filters" for a sort of search results list.
In particular, since I want the pages to be link-able, I'm also setting the form content in the URL query params, via React-Router, or initially setting it on page load via similar mechanism.
The only field so far is "organization_name", a text field, used to set the query param value, and trigger an API request for /endpoint?name={some_name}.
E.g.,
<Field
name="organization_name"
component="input"
type="text"
placeholder="Organization Name"
value={value}
/>
I've tried several things, but here's a recent shot:
I'm grabbing reset, change, and other things from default props. I'm passing in a handleSubmit as required.
handleSubmit works correctly, to do some state updating, set/push the URL query params with React Router, and then make a new API call/update display of new results! Woot!
What I want / expect
In the long run, I would like a "reset filters" button that sets all filter values back to defaults (e.g., set the "name" value to empty string), and re-submits the form (thus triggering handleSubmit).
What I first tried to implement was a button, as such:
<button
name="reset_filters_button"
type="button"
onClick={resetAndSubmit}
disabled={pristine || submitting}
>
Clear Search
</button>
Where resetAndSubmit is defined on the form container as such:
const resetAndSubmit = event => {
reset();
handleSubmit();
};
What actually happens... (submit takes precedence over dispatched events?)
Using the Chrome dev tools debugger, I can clearly see that the reset method is called, and returns it's dispatch(...)'d event. However, the form and state values are not updated before handleSubmit() runs and submits the form.
I think this might have to do with the submit event taking priority?
I have also tried something janky, like importing change (default prop for the container) and defining the reset button thus:
<button
name="reset_filters_button"
type="button"
onClick={() => {
change('organization_name', '');
methodThatDispatchesSubmitAction();
}}
disabled={pristine || submitting}
>
Clear Search
</button>
Which (if I remove methodThatDispatchesSubmitAction()) works correctly to set the field value back to blank, making the form technically "pristine" again as well.
methodThatDispatchesSubmitAction() (if it's not obvious) is bound on the parent via dispatchToProps, and passed in to the form container, where it uses the "remote submit" suggestion, e.g,
// organization_list_filter == name of the Redux-Form to submit.
dispatch(submit('organization_list_filter'));
TL;DR and final question:
How does one properly reset a form and submit its' default/empty values?
Every time I dispatch or directly call Redux Form 'submit', it ends up submitting the form before clearing values from state, or the UI. I have walked through this with a debugger and it's not skipping my call to reset or change. It's like an async/race issue, but I admit I am out of my league in this particular case for sure.
Am I just Straight Up Doing It Wrong?
It is most definitely a race condition issue (or since we aren't actually dealing with threads, an order of events issue).
The reason using a methodThatDispatchesSubmitAction works when your current example does not, is because a dispatched action has the benefit of reading data directly from the redux store. Your example is not reading from the redux store, it's reading from a property that is passed in. Yes, this property comes from the redux store, but the problem you are seeing is that it hasn't been updated in your component yet.
Bear with me as this next piece is not going to be entirely accurate but it should suffice to explain what you are seeing.
Submit is clicked
-> Reset action is dispatched
-> Reducer receives action and returns updated state
-> Handle submit is fired using values prop (old state data still)
Component is updated with new props from redux state
As you can see, the order of events don't allow for an updated state to be given to the property until our click code has finished running. If you've ever watched a video on the JS Event Loop (I highly recommend it), you'll know that our onClick handle will run in full before any other async operations (or sync operations that come after our click) have a chance to run.
There are good reasons why Components aren't given updated props right away but the primary one is performance. You can see that this order is in fact the problem by wrapping the handleSubmit in an async event that fires immediately (it doesn't actually fire immediately, all other sync/async operations queued before it will finish).
const resetAndSubmit = (event) => {
reset();
setImmediate(() => handleSubmit());
}
This changes the order of events as follows:
Submit is clicked
-> Reset action is dispatched
-> Reducer receives action and returns updated state
-> Handle submit is queued on the event loop (not run yet)
Component is updated with new props from redux state
Event loop reaches queued code and runs is
-> Handle submit is fired using values prop (new state data)
Hopefully, this helps you understand why the problem is occurring. As for solutions to fix it. Obviously, you can queue the handle submit as I've shown above. Another option would the one you've described as using a dispatch to perform the submit. A third option would be to use something a bit heavier like redux-thunk or redux-sagas that tie the resetAndSubmit action into a single dispatch. Although honestly, this is the same as option two, just reduced into a single dispatch. Option four, don't use redux for all your data. Obviously, this fourth option comes with trade-offs but my point being, just because you are using redux in a project doesn't mean every single piece of data needs to be in redux. Though it completely defeats the purpose of redux-forms.
I should also add, you are not alone in being confused by this. When you introduce redux, it messes with how you traditionally think about working with code. Normally you think, I do A then B. But with redux, you do A, wait for A's changes to make it through the system, and then you do B. That's where Sagas or Thunks can be nice. You move more logic to the store to act on the dispatch rather than wait for it to all make its way back down to a component via props.
There are a series of components that will reveal themselves after running a custom method viewChildRef.show() on them (I'm using a #ViewChild reference).
The problem is that the state gets updated with a #Select decorator (angular-redux/store) initially before the child component is ready - the callback in the subscription needs to be called after ngAfterViewChecked has triggered.
What is best practice to delay a call to a child component until a specific hook occurs? For those that don't know angular-redux/store, I have a rxjs subscription to play with from the #select decorator. Maybe a delay/debounce set up would work?
I can and have already set a variable readyForShow that is set to true, and then again false once ngAfterViewChecked is called. This is a tedious solution - there must be a better way, and this must be common in the angular-redux world.
My goal is to have something easily and quickly applied when I need to do this elsewhere as well.
Additionally, if there is a ready made solution in ngrx feel free to mention this - it will cause me to consider switching libraries and may help others!
Seems to me, just about every 'real' page will fetch data asynchronously, so I just hook up the template to the observable with the async pipe, let the literal parts display and the async data fill in when it arrives, e.g
<span>Last modified: {{ (fileInfo$ | async)?.lastModified }}</span>
Note the construct - observable and pipe are bracketed, outside the bracket you have the unwrapped object. The Elvis/Safe navigation operator ? which stops evaluation of the property when the object is null.
If the page doesn't look right without data, you can hide it with ngIf or ngShow
<span *ngIf="(lastRefresh$ | async)">Refreshed</span>
Also, with Redux stores the shape of the default/initial store can be important, e.g for an object initialize to {} instead of null, for an array initialize to [] - something that downstream code won't crash on too easily.
I want to map a state, but that state (the name of the state is "task", it is the child of parent state called "timesheet", the "timesheet" state is already defined using componentWillMount) is still undefined UNTIL an action creator has been triggered (click a button) inside my page. But, i already define timesheet.task.map inside jsx, and thus the page cannot load and it shows an error "cannot read property map of undefined".
code
So as you can see here, i want to map a list of 'select' options from a state which is called when an event is triggered (it is still undefined when the page renders), but the page will not load because the timesheet.task is still undefined when the page load the first time
Do you have any idea how to fix this? I was thinking to initialize the state, but i don't know how to do it.Thank you very much!
Very common situation. At the top of your render() - just add a conditional to check if that value is valid or not.. something like:
if (!this.state.timesheet.task) {
return <p> waiting for my value </p> // add any generic component to here that would await your value
}
Depending on your page layout, you might want to just create a small component to inject as a conditional as you await your value to be populated.
I've read up on this a bit but have not received a satisfactory answer. Granted I'm very new to React.
Suppose I have two components, Parent and Child. Parent passes a property to Child, and Child wishes to set a state to that property, as follows:
// in child
getInitialState: function() {
return ({
filters: this.props.filters
});
}
Then, this.state.filters gets modified through the UI, and upon clicking a "Save" button of some sort, saves the updated filters through a Flux-like model via a Store/Dispatcher up through to the Parent, which then re-renders and propagates down the updated this.props.filters down to Child again. This is kind of silly, as Child already has the updated data in its state, but whatever.
In order to check if the "Save" button should be active, I check in Child if:
this.state.filters === this.props.filters
If they are not equal, then the state has been changed from the original default prop. Thus, the "Save" button is activated. Otherwise, the state is identical to the original default prop, and the "Save" button is disabled.
My question is, is this flow an anti-pattern? Seems like it must be.
Within your component you want to know if a value has been modified from the last known value. This approach feels quite reasonable to me. Obviously there are other ways to do this, but I don't see anything wrong with this approach.
About this: "...saves the updated filters through a Flux-like model via a Store/Dispatcher up through to the Parent." Think of it more as stepping out of the rendering flow and into a state-updating flow in your app. The one-way nature of the flux pattern can be a bit more typing, but leads to more manageable code in the long run (e.g. if more than one component needs the updated state).
You are right that the Child already has the soon-to-be official state which will be received again from the Parent as props shortly. The Child can/should render based on that new state even though it's not been saved to the server yet - this is called Optimistic Updates https://facebook.github.io/react/docs/tutorial.html#optimization-optimistic-updates.
As long as the output of the render method doesn't change in the Child component after the new props are received from the Parent, React won't tell the browser to re-render the DOM. This means that your UI renders your desired state, but does so more quickly.