vue nexttick for map - javascript

I have a <div id="map" />
I have a mounted method in which I initialize map.
mounted(){
this.map = L.map(this.mapId, { preferCanvas: true }).setView(
[this.initialLatitudeOriginalPoint, this.intiialLongitudeOriginalPoint],
3
)
L.tileLayer(
tileconfig + '?access_token=' + key
{ maxZoom: 25, id: 'mapbox.streets' }
).addTo(this.map)
}
I have a prop called markers. and I have a watcher for it with immediate true
markers:{
handler(newVal, oldVal){
this.showMarkers()
},
immediate: true
},
Question- Looks like watcher gets called first. before mounted. Then showMarkers function throws error as map won't be defined because watcher got first before mounted. I really need immediate true for that watcher. Is there any way I can know that until map is not defined, watcher should wait?
What I think of: I think of using nextTick. but I don't understand one point about it. If I write this.$nextTick in my watcher's handler function, when will that nextTick callback be called? after dom got updated in this specific component or even in parent and parents of parents? if it doesn't care current component, then nextTick might be a little bit wrong in my case. I just want to understand this last thing about nextTick. any clue?

Firstly, you are correct that immediate will cause the watch to be triggered before the component is mounted. The role of watch is primarily to perform updates to properties based on other properties. So it will wait for all the basic properties (props, data, etc.) to be initialised but it assumes that rendering will need the output of the watch to render correctly. In your case this isn't true.
The way I would write this is:
mounted () {
// ... map stuff ...
this.showMarkers()
},
watch: {
markers: 'showMarkers'
}
If there needs to be a nextTick in there I'd be inclined to do that inside showMarkers rather than making it the caller's problem.
However, you don't have to do it this way. You can do it using immediate and nextTick, as suggested in the question.
So the key thing is to understand when exactly nextTick is called.
If a rendering dependency changes Vue will not re-render the component immediately. Instead it's added to a queue. Once all your other code has finished running Vue will process that queue. This is more complicated than it sounds as various components within the parent/child hierarchy may need updating. Updating a parent may result in the child no longer existing.
Once the queue has been processed and the DOM updated, Vue will call any nextTick callbacks. Any further changes that occur inside the callback will be added to a new rendering queue.
There are a couple of key points to note here.
While Vue may have updated the DOM, the browser won't actually paint all the new nodes until the JavaScript code has finished. That includes any nextTick callbacks. So if nextTick triggers further updates the user will not see the interim state of the page.
All of this is assuming that Vue is tracking the updates and performing the rendering. If you're doing rendering outside of Vue, as you are via Leaflet, then Vue can't do as much to help you.
So let's consider the following:
markers:{
async handler(newVal, oldVal){
await this.$nextTick()
this.showMarkers()
},
immediate: true
},
I've used async/await here rather than passing a callback but the end result is the same.
When the handler is called the first time it is before the component has rendered. The $nextTick will be called after all rendering has completed (including other components). So long as other components don't assume that the markers already exist this shouldn't be a problem, they'll still be created before the user sees anything.
When markers subsequently changes it may not actually trigger any rendering within Vue. Unless you're using markers somewhere within one of your templates it won't be a rendering dependency so no new rendering will be triggered. That's not necessarily a problem as $nextTick will still work even if there's nothing for Vue to render.

Related

React: How does it know when a DOM compoent has been mounted, so that it can call componentDidMount?

In general, i understand that it is difficult for us to know or get notified when a dynamic DOM element has loaded on screen, for e.g, there is no easy way to know whether or not a dynamic textbox is available in the DOM and focus on it, unless we do some kind of polling or do a setTimeout with 0 delay, so that it executes in the next tick when the DOM updation has already happened.
How does React know when the DOM has been updated, so that it can call lifecycle methods like componentDidMount or getSnapshotBeforeUpdate ? Does it use the setTimeout hack ?
EDIT: I understand the basics of the reconciliation algorithm, the linked doc in the comments does not talk bout how exactly react knows when the DOM element has been created/updated by the browser. I understand that the commit phase happens in 2 passes. In the first pass, all the Host(DOM) updates are done and in the second pass the relevant lifecycle methods are called (getSnapshotBeforeUpdated and componentDidMount), but since these 2 passes happen within the same function, how does React know that the DOM changes that have happened in the first pass, are available in the second pass ? Are these DOM updates synchronous ? If I access a DOM element left property in componentDidMount, would it be the new value after the updates are done to the DOM or the old value because when componentDidMount was called the browser might not have updated the DOM ?
To get a better understanding of how React lifecycle methods work, check this cheatsheet.
For your particular case, I would recommend to check the existence of the element (in this case textbox) in DOM. Example:
componentDidMount() {
if (document.getElementById("ID-of-your-textbox")) {
//anything that needs the textbox rendered
}
}
But, if you end up with "my input is not yet rendered right after mount", you should try to put the check to componentDidUpdate method. Example:
componentDidUpdate() {
if (document.getElementById("ID-of-your-textbox")) {
//anything that needs the textbox rendered
}
}

How to initiate a Standard Javascript Callback in Vue.js

How can I initiate this callback on Vue.js on page load?:
lightGallery(document.getElementById('lightgallery'));
One of Vue's life cycle hooks is beforeMount,
Your code can be:
beforeMount(){
lightGallery(document.getElementById('lightgallery'));
},
Use the initialization in your vue component with lifecycle hooks:
Vue.component('lightGallery', function() {
template: '<div>Place template markup here</div>',
mounted: function() {
$(this.$el).lightGallery();
}
});
Then you can just use the component:
<lightGalleryr></lightGallery>
There are different lifecycle hooks Vue provide:
I have listed few are :
beforeCreate: Called synchronously after the instance has just been initialized, before data observation and event/watcher setup.
created: Called synchronously after the instance is created. At this stage, the instance has finished processing the options which means the following have been set up: data observation, computed properties, methods, watch/event callbacks. However, the mounting phase has not been started, and the $el property will not be available yet.
beforeMount: Called right before the mounting begins: the render function is about to be called for the first time.
mounted: Called after the instance has just been mounted where el is replaced by the newly created vm.$el.
beforeUpdate: Called when the data changes, before the virtual DOM is re-rendered and patched.
updated: Called after a data change causes the virtual DOM to be re-rendered and patched.
You can have a look at complete list here.
You can choose which hook is most suitable to you and hook it to call you function like the sample code provided above.

How to ensure I am reading the most recent version of state?

I may be missing something. I know setState is asynchronous in React, but I still seem to have this question.
Imagine following is a handler when user clicks any of the buttons on my app
1. ButtonHandler()
2. {
3. if(!this.state.flag)
4. {
5. alert("flag is false");
6. }
7. this.setState({flag:true});
8.
9. }
Now imagine user very quickly clicks first one button then second.
Imagine the first time the handler got called this.setState({flag:true}) was executed, but when second time the handler got called, the change to the state from the previous call has not been reflected yet -- and this.state.flag returned false.
Can such situation occur (even theoretically)? What are the ways to ensure I am reading most up to date state?
I know setState(function(prevState, props){..}) gives you access to previous state but what if I want to only read state like on line 3 and not set it?
As you rightly noted, for setting state based on previous state you want to use the function overload.
I know setState(function(prevState, props){..}) gives you access to previous state
So your example would look like this:
handleClick() {
this.setState(prevState => {
return {
flag: !prevState.flag
};
});
}
what if I want to only read state like on line 3 and not set it?
Let's get back to thinking why you want to do this.
If you want to perform a side effect (e.g. log to console or start an AJAX request) then the right place to do it is the componentDidUpdate lifecycle method. And it also gives you access to the previous state:
componentDidUpdate(prevState) {
if (!prevState.flag && this.state.flag) {
alert('flag has changed from false to true!');
}
if (prevState.flag && !this.state.flag) {
alert('flag has changed from true to false!');
}
}
This is the intended way to use React state. You let React manage the state and don't worry about when it gets set. If you want to set state based on previous state, pass a function to setState. If you want to perform side effects based on state changes, compare previous and current state in componentDidUpdate.
Of course, as a last resort, you can keep an instance variable independent of the state.
React's philosophy
The state and props should indicate things the components need for rendering. React's render being called whenever the state and props change.
Side Effects
In your case, you're causing a side effect based on user interaction which requires specific timing. In my opinion, once you step out of rendering - you probably want to reconsider state and props and stick to a regular instance property which is synchronous anyway.
Solving the real issue - Outside of React
Just change this.state.flag to this.flag everywhere, and update it with assignment rather than with setState. That way you
If you still have to use .state
You can get around this, uglily. I wrote code for this, but I'd rather not publish it here so people don't use it :)
First promisify.
Then use a utility for only caring about the last promise resolving in a function call. Here is an example library but the actual code is ~10LoC and simple anyway.
Now, a promisified setState with last called on it gives you the guarantee you're looking for.
Here is how using such code would look like:
explicitlyNotShown({x: 5}).then(() => {
// we are guaranteed that this call and any other setState calls are done here.
});
(Note: with MobX this isn't an issue since state updates are sync).

Can I call APIs in componentWillMount in React?

I'm working on react for last 1 year. The convention which we follow is make an API call in componentDidMount, fetch the data and setState after the data has come. This will ensure that the component has mounted and setting state will cause a re-render the component but I want to know why we can't setState in componentWillMount or constructor
The official documentation says that :
componentWillMount() is invoked immediately before mounting occurs. It
is called before render(), therefore setting state in this method will
not trigger a re-rendering. Avoid introducing any side-effects or
subscriptions in this method.
it says setting state in this method will not trigger a re-rendering, which I don't want while making an API call. If I'm able to get the data and able to set in the state (assuming API calls are really fast) in componentWillMount or in constructor and data is present in the first render, why would I want a re-render at all?
and if the API call is slow, then setState will be async and componentWillMount has already returned then I'll be able to setState and a re-render should occur.
As a whole, I'm pretty much confused why we shouldn't make API calls in constructor or componentWillMount. Can somebody really help me understand how react works in such case?
1. componentWillMount and re-rendering
Compare this two componentWillMount methods.
One causes additional re-render, one does not
componentWillMount () {
// This will not cause additional re-render
this.setState({ name: 'Andrej '});
}
componentWillMount () {
fetch('http://whatever/profile').then(() => {
// This in the other hand will cause additional rerender,
// since fetch is async and state is set after request completes.
this.setState({ name: 'Andrej '});
})
}
.
.
.
2. Where to invoke API calls?
componentWillMount () {
// Is triggered on server and on client as well.
// Server won't wait for completion though, nor will be able to trigger re-render
// for client.
fetch('...')
}
componentDidMount () {
// Is triggered on client, but never on server.
// This is a good place to invoke API calls.
fetch('...')
}
If you are rendering on server and your component does need data for rendering, you should fetch (and wait for completion) outside of component and pass data thru props and render component to string afterwards.
ComponentWillMount
Now that the props and state are set, we finally enter the realm of Life Cycle methods
That means React expects state to be available as render function will be called next and code can break if any mentioned state variable is missing which may occur in case of ajax.
Constructor
This is the place where you define.
So Calling an ajax will not update the values of any state as ajax is async and constructor will not wait for response. Ideally, you should use constructor to set default/initial values.
Ideally these functions should be pure function, only depending on parameters. Bringing ajax brings side effect to function.
Yes, functions depend on state and using this.setState can bring you such issues (You have set value in state but value is missing in state in next called function).
This makes code fragile. If your API is really fast, you can pass this value as an argument and in your component, check if this arg is available. If yes, initialise you state with it. If not, set it to default. Also, in success function of ajax, you can check for ref of this component. If it exist, component is rendered and you can call its state using setState or any setter(preferred) function.
Also remember, when you say API calls are really fast, your server and processing may be at optimum speed, but you can never be sure with network.
If you need just data only at first run and if you are ok with that. You can setState synchronously via calling a callback.
for eg:
componentWillMount(){
this.setState({
sessionId: sessionId,
}, () => {
if (this.state.hasMoreItems = true) {
this.loadItems() // do what you like here synchronously
}
});
}

How do I update the state (using ReactJS) if I should not call setState in componentWillUpdate?

When I setState in componentWillUpdate, componentWillUpdate runs in an infinite loop that doesn't stop getting triggered.
This never gives my render a chance to reflect my changes. How can I change the state if I shouldn't use componentWillUpdate?
Edit: I already have some understanding that setState should not be called in componentWillUpdate. I'm just confused what I should do as an alternative.
Edit #2: I started with componentWillReceiveProps but I can't seem to trigger this function when my Parent component changes state. I provide that state from the parent as a props to my child.
First thing to do is to check official documentation for this method (link). Where you can read when the function is actually called.
Then read common mistake(note):
You cannot use this.setState() in this method. If you need to update state in response to a prop change, use componentWillReceiveProps instead.
You change the state and React automatically calls componentWillUpdate.
I understand this is cautioned against in the guide but I am not sure I can see the problem with calling setState from within componentWillUpdate. True, it may result in infinite recursion unless of course you provide a bottom to that recursion (a way to break out of it). E.g. a way could be to check the second (nextState) parameter in componentWillUpdate and not invoke setState again if some condition is met (which is when the recursion ends).
As a minimal example, imagine a component that has no properties at all, only two pieces of state. Further imagine that the second piece of state is asynchronously obtained from the first. E.g. the first piece of state could be some parameter to provide to an Ajax call, and the second piece of state is the result of that call. So basically you call this.setState to configure the parameters and then inside componentWillUpdate you can do something like the following (to keep things simple I use a window.setTimeout as a placeholder for an Ajax call):
const ComponentWithAsyncState = React.createClass({
getInitialState: function() {
return {
ajaxParams: '',
ajaxResult: ''
};
},
setAjaxParams: function(params) {
this.setState({ajaxParams: params});
},
componentWillUpdate: function(_, nextState) {
if (nextState.ajaxParams!=this.state.ajaxParams)
window.setTimeout(function imagineThisIsAjax() {
this.setState({ajaxResult: `result from ${nextState.ajaxParams}`});
}.bind(this), 2000);
},
When, (e.g. through some controls managed by this component) the ajaxParams change, the sequence of actions will be something like (where ~~> denotes asynchronicity):
setAjaxParams --> this.setState --> componentWillUpdate ~~> imagineThisIsAjax --> this.setState --> componentWillUpdate
I.e. the second call to componentWillUpdate will not result in a further this.setState and thus the recursion will end there.

Categories

Resources