initialize state react, to solve undefined when the page first rendered - javascript

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.

Related

Vue JS: Is it safe to access state right after setting it?

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'

How to enable tracking deep changes in component props

I have a component who initialized like this
<custom :opts="{map: false}"></custom>
and there is HTML similar to this
<template id="custom">
<div v-if="opts.map">
I'm awesome
</div>
<button v-on:click="show"></button>
</template>
where
function show(){
this.opts = {map:true} // (1) <-- This is working and I could see hidden div
this.opts.map = true // (2) <-- For some reason not working
Vue.set(this.opts, 'map', true) // (3) <-- Still not working
}
So my question is why variant 2 doesn't work and what should I change to make my control react to value reset on a button click. Or a proper explanation why (1) is working, but (2) isn't - also will be accepted as an answer.
The real problem with the code (all 3 versions) is changing a component's property from within a component. In idiomatic Vue, only the parent should change properties. If a component needs to effect a change, it should emit an event to the parent and let the parent make the necessary changes. Otherwise, there is ambiguity in which component "owns" the property.
One Way Data Flow
All props form a one-way-down binding between the child property and the parent one: when the parent property updates, it will flow down to the child, but not the other way around.
Sending Messages to Parents with Events
Can be off base here but I believe this happens because in vue component props are not reactive, so their objects are not being observed in depth. Or rather they are "a little bit reactive", reassigning the root prop does cause the DOM update but is not expected to be done manually and you'll see a warning when doing such on development build:
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "..."
And for as why props are not completely reactive in the first place: https://v2.vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow
To work around the whole issue you must pass any necessary props to the component data and if those props were passed as nested objects you might also want to completely avoid mutating them from within the component since it will propagate to the parent which, unless clearly mentioned, can be a source of bad news.

VueJS if / else: the else loads first, then the if loads

The problem:
VueJS is briefly showing the "else" condition before the "if" condition loads. This is causing an unintended experience for the user because they should never see the "else" when the "if" is truthy. They should only see the "if" if the "if" is true.
The detail:
I have a custom online store and many different markets. I get back an item count from my server and if that number is zero then I say there are no items for that country, in other words the country is not open yet for this store. If the number is not zero then don't show the message and actually show the items.
Here is a slimmed down version of my code:
HTML
<div v-if="count !== 0">Items are in the store, here they are!</div>
<div v-else>Sorry no items in the store.</div>
JS
mounted() {
// Checks the location and gets items from server.
if (this.location) {
this.getItemsForHomeView();
}
}
I found this: Understanding Vue.js Lifecycle Hooks but it didn't help because when I tried moving the function out of the mounted() and into any of the earlier ones, like beforeCreate() or created() or beforeMount() I get the same result.
In addition to handling an empty item count once the data has resolved, you need to handle the case of there being no data yet to count. You didn't mention using Vue Router, but this discussion of handling async data still applies: you can either have the async fetch prevent the component from rendering at all, using a beforeMount or beforeRouteEnter hook, or wrap all the item list markup in another conditional, which is only rendered after your data has been fetched, and something like isLoaded (local state) is set to true.

React - How can I wrap and element depending on a boolean parameter?

I'm using react-bootstrap tooltip to build a react component. According to react-boostrap specifications, I need to wrap my element with an OverlayTrigger element, like this:
<OverlayTrigger overlay={this.getTooltipElement()} placement='left'>
//element
</OverlayTrigger>
This works just fine, but my component requires to show the tooltip when hovered (usual behavior) AND only if a boolean variable is set to true. I've tried this:
if (booleanParameter) {
return (<OverlayTrigger overlay={this.getTooltipElement()} placement='left'>
{myElementVariable};
</OverlayTrigger>)
} else {
return myElementVariable;
}
But when the tooltip is instructed to show (by the boolean parameter), I get the following warning:
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the undefined component.
Important: getTooltipElement() uses component state fields.
... and the component starts to act erratically. How should I go to solve my problem? How can I wrap and element depending on a boolean parameter? Is there another, less elaborated solution?
I believe the approach you are using is fine, the warning essentially tells you that somewhere - possibly in whatever getTooltipElement() returns, you're calling setState after the component unmounted. You fix this by checking whether the component is still mounted before you set the state:
if(this.isMounted())
this.setState({...});
By the way, if you give your components a 'displayName' attribute, the warning will actually tell you in which component it happened, making it easier to find.

Possible React Antipattern: Setting initial state from props

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.

Categories