In the Vue docs it is mentioned that computed properties are smartly cached as opposed to using regular methods:
In comparison, a method invocation will always run the function whenever a re-render happens. Why do we need caching? Imagine we have an expensive computed property...
My question is: Do Vuex watched properties also having caching like computed properties? (including Vuex watching, eg. using vm.$store.watch...)
While saurabh's answer is not wrong, I feel that it does not really answer the question.
The answer would be: no, watchers are not "cached" - that makes no sense, because watchers are functions that have side effects, but don't have return values and can't be used as properties.
So it is not necessary or sensible to cache anything for watchers.
But yes, both are only executed when the watched data changes.
Behaviour of watchers will be same as behaviour of computed as computed is internally implemented using watchers. When one defines a computed property, Vue internally sets a watcher on the variables used in the computed property, see the code below from source:
function makeComputedGetter (getter: Function, owner: Component): Function {
const watcher = new Watcher(owner, getter, noop, {
lazy: true
})
return function computedGetter () {
if (watcher.dirty) {
watcher.evaluate()
}
if (Dep.target) {
watcher.depend()
}
return watcher.value
}
}
So code written in computed or watch blocks will be executed only once, when the reactive data changes.
Related
I'm curious about passing props into setup and what are best practices to update variables/templates based on property changes.
I'm curious about reactive and computed.
For example:
setup(props) {
// Setup global config settings
const config = computed(() => {
return {
// See if the component is disabled
isDisabled: props.disabled, // (1)
// Test for rounded
isRounded: props.rounded // (2)
}
})
return { config }
}
Should config.isDisabled and config.isRounded be wrapped in their own computed function as they are both different and independent? However, it is easy to just stick them into one big function. What is best practice in this regard?
Does the entire config function evaluate once a single property changes within the function or can it recognize the change and update what is required?
Per docs, reactive is deeply reactive and used for objects, however, I've noticed it doesn't update to property changes. Therefore, I've been treating it more like data in Vue 2. Am I missing something or is this correct treatment?
You do not have to wrap props with computed at all, as they should be already reactive and immutable.
You also do not have to return config from your setup function as all props passed to your component should be automatically exposed to your template.
The computed function is evaluated only once and then Vue3 uses Proxy to observe changes to values and update only what's required. If you need to run a function every time a property changes you can use watchEffect.
Vue3 reactive is actually deep and works fine on objects. It should track all changes, unless you are trying to change the original object (the target of reactive function).
I understand that React tutorials and documentation warn in no uncertain terms that state should not be directly mutated and that everything should go through setState.
I would like to understand why, exactly, I can't just directly change state and then (in the same function) call this.setState({}) just to trigger the render.
E.g.: The below code seems to work just fine:
const React = require('react');
const App = React.createClass({
getInitialState: function() {
return {
some: {
rather: {
deeply: {
embedded: {
stuff: 1,
},
},
},
},
},
};
updateCounter: function () {
this.state.some.rather.deeply.embedded.stuff++;
this.setState({}); // just to trigger the render ...
},
render: function() {
return (
<div>
Counter value: {this.state.some.rather.deeply.embedded.stuff}
<br></br>
<button onClick={this.updateCounter}>Increment</button>
</div>
);
},
});
export default App;
I am all for following conventions but I would like to enhance my further understanding of how ReactJS actually works and what can go wrong or is it sub-optimal with the above code.
The notes under the this.setState documentation basically identify two gotchas:
That if you mutate state directly and then subsequently call this.setState this may replace (overwrite?) the mutation you made. I don't see how this can happen in the above code.
That setState may mutate this.state effectively in an asynchronous / deferred way and so when accessing this.state right after calling this.setState you are not guaranteed to access the final mutated state. I get that, by this is not an issue if this.setState is the last call of the update function.
This answer is to provide enough information to not change/mutate the state directly in React.
React follows Unidirectional Data Flow. Meaning, the data flow inside react should and will be expected to be in a circular path.
React's Data flow without flux
To make React work like this, developers made React similar to functional programming. The rule of thumb of functional programming is immutability. Let me explain it loud and clear.
How does the unidirectional flow works?
states are a data store which contains the data of a component.
The view of a component renders based on the state.
When the view needs to change something on the screen, that value should be supplied from the store.
To make this happen, React provides setState() function which takes in an object of new states and does a compare and merge(similar to object.assign()) over the previous state and adds the new state to the state data store.
Whenever the data in the state store changes, react will trigger an re-render with the new state which the view consumes and shows it on the screen.
This cycle will continue throughout the component's lifetime.
If you see the above steps, it clearly shows a lot of things are happening behind when you change the state. So, when you mutate the state directly and call setState() with an empty object. The previous state will be polluted with your mutation. Due to which, the shallow compare and merge of two states will be disturbed or won't happen, because you'll have only one state now. This will disrupt all the React's Lifecycle Methods.
As a result, your app will behave abnormal or even crash. Most of the times, it won't affect your app because all the apps which we use for testing this are pretty small.
And another downside of mutation of Objects and Arrays in JavaScript is, when you assign an object or an array, you're just making a reference of that object or that array. When you mutate them, all the reference to that object or that array will be affected. React handles this in a intelligent way in the background and simply give us an API to make it work.
Most common errors done when handling states in React
// original state
this.state = {
a: [1,2,3,4,5]
}
// changing the state in react
// need to add '6' in the array
// bad approach
const b = this.state.a.push(6)
this.setState({
a: b
})
In the above example, this.state.a.push(6) will mutate the state directly. Assigning it to another variable and calling setState is same as what's shown below. As we mutated the state anyway, there's no point assigning it to another variable and calling setState with that variable.
// same as
this.state.a.push(6)
this.setState({})
Many people do this. This is so wrong. This breaks the beauty of React and is bad programming practice.
So, what's the best way to handle states in React? Let me explain.
When you need to change 'something' in the existing state, first get a copy of that 'something' from the current state.
// original state
this.state = {
a: [1,2,3,4,5]
}
// changing the state in react
// need to add '6' in the array
// create a copy of this.state.a
// you can use ES6's destructuring or loadash's _.clone()
const currentStateCopy = [...this.state.a]
Now, mutating currentStateCopy won't mutate the original state. Do operations over currentStateCopy and set it as the new state using setState().
currentStateCopy.push(6)
this.setState({
a: currentStateCopy
})
This is beautiful, right?
By doing this, all the references of this.state.a won't get affected until we use setState. This gives you control over your code and this'll help you write elegant test and make you confident about the performance of the code in production.
To answer your question,
Why can't I directly modify a component's state?
Well, you can. But, you need to face the following consequences.
When you scale, you'll be writing unmanageable code.
You'll lose control of state across components.
Instead of using React, you'll be writing custom codes over React.
Immutability is not a necessity because JavaScript is single threaded, but it's a good to follow practices which will help you in the long run.
PS. I've written about 10000 lines of mutable React JS code. If it breaks now, I don't know where to look into because all the values are mutated somewhere. When I realized this, I started writing immutable code. Trust me! That's the best thing you can do it to a product or an app.
The React docs for setState have this to say:
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.
setState() will always trigger a re-render unless conditional rendering logic is implemented in shouldComponentUpdate(). If mutable objects are being used and the logic cannot be implemented in shouldComponentUpdate(), calling setState() only when the new state differs from the previous state will avoid unnecessary re-renders.
Basically, if you modify this.state directly, you create a situation where those modifications might get overwritten.
Related to your extended questions 1) and 2), setState() is not immediate. It queues a state transition based on what it thinks is going on which may not include the direct changes to this.state. Since it's queued rather than applied immediately, it's entirely possible that something is modified in between such that your direct changes get overwritten.
If nothing else, you might be better off just considering that not directly modifying this.state can be seen as good practice. You may know personally that your code interacts with React in such a way that these over-writes or other issues can't happen but you're creating a situation where other developers or future updates can suddenly find themselves with weird or subtle issues.
the simplest answer to "
Why can't I directly modify a component's state:
is all about Updating phase.
when we update the state of a component all it's children are going to be rendered as well. or our entire component tree rendered.
but when i say our entire component tree is rendered that doesn’t mean that the entire DOM is updated.
when a component is rendered we basically get a react element, so that is updating our virtual dom.
React will then look at the virtual DOM, it also has a copy of the old virtual DOM, that is why we shouldn’t update the state directly, so we can have two different object references in memory, we have the old virtual DOM as well as the new virtual DOM.
then react will figure out what is changed and based on that it will update the real DOM accordingly .
hope it helps.
It surprises me that non of the current answers talk about pure/memo components (React.PureComponent or React.memo). These components only re-render when a change in one of the props is detected.
Say you mutate state directly and pass, not the value, but the over coupling object to the component below. This object still has the same reference as the previous object, meaning that pure/memo components won't re-render, even though you mutated one of the properties.
Since you don't always know what type of component you are working with when importing them from libraries, this is yet another reason to stick to the non-mutating rule.
Here is an example of this behaviour in action (using R.evolve to simplify creating a copy and updating nested content):
class App extends React.Component {
state = { some: { rather: { deeply: { nested: { stuff: 1 } } } } };
mutatingIncrement = () => {
this.state.some.rather.deeply.nested.stuff++;
this.setState({});
}
nonMutatingIncrement = () => {
this.setState(R.evolve(
{ some: { rather: { deeply: { nested: { stuff: n => n + 1 } } } } }
));
}
render() {
return (
<div>
Normal Component: <CounterDisplay {...this.state} />
<br />
Pure Component: <PureCounterDisplay {...this.state} />
<br />
<button onClick={this.mutatingIncrement}>mutating increment</button>
<button onClick={this.nonMutatingIncrement}>non-mutating increment</button>
</div>
);
}
}
const CounterDisplay = (props) => (
<React.Fragment>
Counter value: {props.some.rather.deeply.nested.stuff}
</React.Fragment>
);
const PureCounterDisplay = React.memo(CounterDisplay);
ReactDOM.render(<App />, document.querySelector("#root"));
<script src="https://unpkg.com/react#17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/ramda#0/dist/ramda.min.js"></script>
<div id="root"></div>
To avoid every time to create a copy of this.state.element you can use update with $set or $push or many others from immutability-helper
e.g.:
import update from 'immutability-helper';
const newData = update(myData, {
x: {y: {z: {$set: 7}}},
a: {b: {$push: [9]}}
});
setState trigger re rendering of the components.when we want to update state again and again we must need to setState otherwise it doesn't work correctly.
My current understanding is based on this and this answers:
IF you do not use shouldComponentUpdate or any other lifecycle methods (like componentWillReceiveProps, componentWillUpdate, and componentDidUpdate) where you compare the old and new props/state
THEN
It is fine to mutate state and then call setState(), otherwise it is not fine.
In current (2020) Angular, I have two components that are intended to share the state of activeProject through a service. I have the following defined on an ApplicationProjectService:
private activeProjectSource = new BehaviorSubject(undefined);
activeProject$ = this.activeProjectSource.asObservable();
set activeProject(v: any) {
this.activeProjectSource.next(v);
}
get activeProject() {
return this.activeProjectSource.value;
}
I'm using BehaviorSubject in the service since I want components to get the current value upon subscribing without any change. The getter/setter is there because I was doing some other binding directly to a service property, which I've since learned is not recommended.
The two sibling components that eventually trace back to a common parent, but I'm not using #Input() or #Output() or any parameter passing in the DOM:
this.appProjectService.activeProject$.subscribe(activeProject => {
this.activeProject = activeProject;
});
Each component is binding to the this.activeProject property in their respective component using [(ngModel)]:
<input type="checkbox" [(ngModel)]="activeProject.someProperty">
Question
If each component obtained what I thought was a copy of activeProject through this.appProjectService.activeProject$.subscribe(), how is it working that a change to the local property in one component is reflected in the other? In the end this is the behavior I want, but I can't understand why it works. Is there some passing by reference that I'm not understanding in rxjs observables?
sIf you have 2 components, the both local variables activeProject use the same reference of activeProject. ngModel is bound to a property of this reference. So it's working, because a change in a component only update the property of the reference, and does not change the reference. You can even use a variable activeProject without wrapping it in a BehaviorSubject.
I know this should be in comment but this much of letters comment won't accept.
Forget about RxJS for a while.
Now you have getter and setter for your property.
You set activeProjectValue in your service.
Now when you subscribe it in one component, you will get the object which will be passed by reference. Same for the other component. As both components accessing same object they are passed by reference.
If you have to break the reference, to use it differently.
Also each component obtained what I thought was a copy of activeProject .... this means they copy by refenrence of object.
I know, you know how to break reference, but this is just for sake for future viewers
To break the reference of object you can use JSON.parse(JSON.stringify(*ObjectName*)
In your example
this.appProjectService.activeProject$.subscribe(activeProject => {
this.activeProject = JSON.parse(JSON.stringify(activeProject));
});
I am making an app just for practice and i have a doubt in a component's function "didReceiveAttr". When i pass my MODEL in my template and then i erase some element in it the function doesnt work, but if i pass "model.length" in the template and then erase something the function work!
My component template
<h1>Tasks ({{totalTask}})</h1>
My component JS
totalTask: null,
didReceiveAttrs(){
this._super(...arguments);
this.set('totalTask', this.get('model.length'));
console.log(this.get('model'));
}
My primary template
{{task-list model=model}}
or
{{task-list model=model.length}}
This is indeed the expected behavior; just look at Ember guide about how didReceiveAttrs works. It is clearly stated that "didReceiveAttrs hook is called every time a component's attributes are updated". When you add to or remove from an array the array itself does not change; hence didReceiveAttrs is not executed. It is only executed when the initial assignment to model is performed.
I prepared this twiddle to illustrate you a better ember way to handle this case. You should rely on computed properties as much as you can; hence I added computedTotalTask as a computed property to my-component.js and it relies on model.length as you can see.
{{task-list modelLength=model.length}}
Here you are assigningmodel.length as modelLength property to the component. so initially didReceiveAttrs will be called as component is receiving modelLength property and when you add one more element to model then modelLength property itself changed so this will invoke didReceiveAttrs before re-render.
{{task-list modelTaskList=model}}
Here modelTaskList is pointing to array, so when you add/remove item through KVO compliant method such as pushObject it will be reflected in component too. but the modelTaskList is still pointing to the same array so didReceiveAttrs hook will not be called.
Suppose if you assigned different array then you can see the didReceiveAttrs is called.
You could always just set this as a computed property, ensuring updates in the event of the bound variable being updated.
Within your component, set up a computed property that will watch for a change to your model, then update the variable modelLength with the change
modelLength: Ember.computed('model', function(){
return this.get('model').length;
}
Then, within your handlebars template, reference this length
<h1>Tasks{{#if modelLength}} ({{modelLength}}){{/if}}</h1>
Is it a good practice to do this in ReactJS?
var Component = React.createClass({
render: function () {
return (<div></div>);
},
field: 'value', // is this safe?
method: function () {
// do something with field
}
})
Before starting to suggest that I should use this.props or this.state, for me it's not the case, because those are fields that do not affect rendering in any way directly, they just work together to control the rendering.
I would like to use the React class as I do with regular javascript 'classes'.
My main concern here is how those fields and methods are handled inside React, and if the fields are set on the instance itself or directly on the prototype, which would not be suitable at all for what I need.
I ran a quick test and it seems that the fields are set on the instance, and the methods on the prototype, which is ideal. But is this the expected and documented behavior? And is this safe for future versions?
I think it can work the way you are doing and that it's safe. However if I understand well you are proceeding data calculation/transformation directly in the view.
So I would advise that you remove this logic from the view and treat it in the model part of a mvc or mv*, in your backbone models, or in your flux store for example.
This way you won't be mixing data transformation logic and pure rendering.
I would say so, I have been using things like this for a while and have not seen any issues. For example, let's say you want a handler of some sort that you want to pass to nested components, you would create the function in this component and pass it as a prop to a child. I believe they have examples that use similar concept in the ReactJS Facebook site.
Under the hood React is just looping through the properties of the object you pass to createClass and copying them to the prototype of the Component. Primitive values like strings or numbers obviously cannot be copied by reference, so don't get shared across all instances, whereas objects, functions, arrays and so on will.
If you want to work with values that are just local to the component instance you need to use the state API. I'm not sure what you mean by "[state and props] do not affect rendering in any way directly, they just work together to control the rendering". The whole point of props and state is that they work together to generate values to be used when (re)rendering.
https://facebook.github.io/react/docs/component-api.html
A React component should only render in response to either changing props or changing state. You cannot/shouldn't trigger a re-render by mutating other fields directly.
You need to think of your component as something closer to a pure function. State and props go in at the top, and static VDOM/HTML comes out.
I would re-write your example as,
var Component = React.createClass({
getInitialState: function () {
return {field: 'value'};
},
render: function () {
var field = this.state.field;
return (<div>{field}</div>);
},
method: function () {
var field = this.state.field;
// do something with field
this.setState({field: 'anotherValue'});
}
})