Setter function and setState - reactjs

I have seen the following code:
class Welcome extends React.Component {
constructor(props) {
super(props);
this.state = {
name: ''
}
}
set name(newValue: string) {
this.setState({ name: newValue });
}
get name(): string {
return this.state.name
}
}
The setter function puzzles me. Could anyone tell me when the setter function is called? Will it be called by this.setState({ name: ...? If so, will it be infinite?
Similarly for the getter function. Will it be an infinite call?

Could anyone tell me when the setter function is called?
This is called when attempting to set value to this.name. This is a javascript feature and not related to React.
React ignores all changes to state except when done via setState. Changing name using this.name = 'newName' isn't normally a valid method of modifying state. All state changes will HAVE to occur via setState. In this code, a custom setter method is defined so that this.name = 'newName' calls this.setState.
There is no conflict in this case. Calling setter method during assignment is javascript feature, React plays no role in it. The setter method modifies the state using setState which then notifies react of the change.
The getter function is called every time you try to access this.name. Since the getter function returns this.state.name, it is as-if this.name expands to this.state.name.

Related

set state in a callback of an async function

I am new to React, so bear with me please. I have a component that calls another component that takes a property. This property will get it's value on a callback of a function, something like this:
render(){
myFunc((p) => {
if(!_.isEqual(p, this.state.myProp))
this.setState({myProp: p})
});
return <MyComponent myProp={this.state.myProp}/>
}
myFunc will or will not make an API request and depending on that will call the callback sooner or later. This seems to work fine when API request is made and the callback takes longer to return. However, when the request is not needed and callback returns instantaneously (or almost) I am getting a Warning: Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.
What am I doing wrong and what is the right way to approach this? Where would be the right place to put this code? Basically what I need is to re-render MyComponenent if this.state.myProp changes
You shouldn't be calling setState inside the render method, you might end up having an infinite loop.
The call to myFunc should be somewhere else (depending on the business logic you have). When the function finishes, it will update the state and then trigger a re-render so MyComponent will get the latest value.
UPDATE
I don't know which conditions will require calling myFunc again, but you can do:
state = {
myProp: null // or some other value that MyComponent can handle as a null state
}
componentDidMount () {
myFunc((p) => {
if(!_.isEqual(p, this.state.myProp)) // This is needed only if you get null back from the callback and you don't want to perform an unnecesary state update
this.setState({myProp: p})
}
}
render(){
const { myProp } = this.state
// You can also do if (!myProp) return null
return <MyComponent myProp={myProp}/>
}

Can you use this.setState in a constructor object inside a react component?

I want this firebase on value event listener to be running inside this component at all times listening for changes in the database to then update the state. I was looking at examples and I only found that I can add this particular firebase event listener inside the constructor object. However, when I set the state I receive an error as such null is not an object(evaluating this.setState). Any Ideas on how I can get around this?
constructor(props) {
super(props);
this.props = props;
this.actions = commentActions;
this.state = {
comments: [],
loadingComments: true,
lastCommentUpdate: null,
review: props.review ? props.review : null,
login: null,
id: props.id
};
this.scrollIndex = 0;
database.ref("comments/").on("value", function(snapshot) {
console.log('DATA: ', typeof snapshot)
// console.log(this.state.comments )
this.setState({
comments:[...this.state.comments, snapshot]
})
})
}
I think you're problem is here:
database.ref("comments/").on("value", function(snapshot) {
console.log('DATA: ', typeof snapshot)
// console.log(this.state.comments )
this.setState({
comments:[...this.state.comments, snapshot]
})
})
this bit: function(snapshot) , is binding the "this" context to the caller, which is ".on" (guessing). So, we have to remove that. Make sure you call "this" in the context of the constructor, by changing it to an anonymous function, which doesn't have a "this" context of its own.. SO, the "this" is gotten from the surrounding object.
Change to: This allows for the "this" context to to be owned by parent object.
database.ref("comments/").on("value",(snapshot) => {
console.log('DATA: ', typeof snapshot)
console.log(this.state.comments )
this.setState({
comments:[...this.state.comments, snapshot]
})
})
IF this dooesn't work, then I assume "this" is not defined yet, within the confines of the constructor call, so you'll probably have better luck putting it in "ComponentDidMount" lifecycle method.
You can use setState inside a constructor, and if it is called after the end of the current execution stack as it would with a database call, it would trigger a re-render. So yes your approach would work if not for the other errors. I do however recommend doing data fetching in the componentDidMount method of a component as sited in the React.Component documentation.
componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request.

why do you need to bind a function in a constructor

I have a question relavent to this code: https://github.com/reactjs/redux/blob/master/examples/async/containers/App.js
specifically:
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
this.handleRefreshClick = this.handleRefreshClick.bind(this)
}
I guess its a 2 part question.
Why do I need to set handle change as an instance of class this.handleChange =, can't I just use static functions for handleChange and call it directly with in the class onClick={handleRefreshClick}> ?
I have no idea whats going on here: this.handleRefreshClick.bind(this)
Thanks
Answered in reverse order...
this.handleRefreshClick.bind(something) returns a new function, in which references to this will refer to something. This is a way of saving the current value of this, which is in scope during the call to the constructor, so that it can be used later when the function is called.
If your functions don't require access to the state of your component, then sure, you don't need to bind them.
The argument in favour of adding these lines to the constructor is so that the new bound functions are only created once per instance of the class. You could also use
onClick={this.handleRefreshClick.bind(this)}
or (ES6):
onClick={() => this.handleRefreshClick()}
but either of these methods will create a new function every time the component is re-rendered.
The reason why it's being done, is to bind the this keyword to that object. Like Tom said, calling a function from a class doesn't mean it's being called with the context of the object that created that function.
I think you might be getting confused because in the React examples/tutorials, using React.createClass() DOES bind this automatically for you. So you might be wondering why React.createClass() does it, but doesn't with ES6 class syntax.
This is because React didn't want to mess with ES6 specifications (binding this to functions from its class is not in the ES6 class spec), but at the same time, wanted to give its users the convenience of ES6 class syntax. You can read more about this below.
Github issue
Hopefully that sheds some light on why that happens.
this depends how the function is called, not how/where it is created.
When you look at the code, you see two "this", why? Seems weird, right?
The thing is it is not about how it seems. It is about how it is called.
You are basically saying. Hey, when somebody calls you remember this means this class. not something else.
When somebody calls your class like: x.yourClass().bind(this) you are saying this is not x but the class itself(with props and states etc.).
Quick note, even when you call directly the class like yourClass() you are actually calling window.yourClass() on browser, also that is why in this case the this is window.
These 2 functions handleChange and handleRefreshClick are passed down as props to other components ,
They are bind to this because when the child component will call these functions they will always execute with the APP context.
You can remove these functions from the class but still you need to bind this since you would be updating some parts of your APP
All the answers here are good, but for clarity regarding:
I have no idea whats going on here: this.handleRefreshClick.bind(this)
I think an example is best in describing the difference in behaviour.
// Class where functions are explicitly bound to "this" specific object
var Bindings = class {
constructor() {
this.Firstname = "Joe"
this.Surname = "Blow"
this.PrettyPrint = this.PrettyPrint.bind(this)
this.Print = this.Print.bind(this)
}
Print(inputStr) {
console.log(inputStr)
console.log(this)
}
PrettyPrint() {
this.Print(`${this.Firstname} ${this.Surname}`)
}
}
// Class where "this" context for each function is implicitly bound to
// the object the function is attached to / window / global
// identical class, except for removing the calls to .bind(this)
var NoBindings = class {
constructor() {
this.Firstname = "Joe"
this.Surname = "Blow"
}
Print(inputStr) {
console.log(inputStr)
console.log(this)
}
PrettyPrint() {
this.Print(`${this.Firstname} ${this.Surname}`)
}
}
var bindings = new Bindings()
var noBindings = new NoBindings()
bindings.PrettyPrint()
// > "Joe Blow"
// > Object { Firstname: "Joe", Surname: "Blow", PrettyPrint: PrettyPrint(), Print: Print() }
noBindings.PrettyPrint()
// > "Joe Blow"
// > Object { Firstname: "Joe", Surname: "Blow" }
// noBindings has both functions & everything works as we expect,
// if this is all you're doing, then there's practically little difference,
// but if we separate them from the original "this" context...
var b = { PPrint: bindings.PrettyPrint }
var nb = { PPrint: noBindings.PrettyPrint }
b.PPrint()
// > "Joe Blow"
// > Object { Firstname: "Joe", Surname: "Blow", PrettyPrint: PrettyPrint(), Print: Print() }
// PPrint calls "PrettyPrint" where "this" references the original "bindings" variable
// "bindings" has a function called "Print" which "PrettyPrint" calls
try {
nb.PPrint()
} catch (e) {
console.error(e);
}
// > TypeError: this.Print is not a function
// PPrint calls "PrettyPrint" where "this" references the new "nb" variable
// due to different "this" context, "nb" does not have a function called "Print", so it fails
// We can verify this by modifying "bindings" and seeing that it's reflected in "b"
bindings.Surname = "Schmo"
b.PPrint()
// > "Joe Schmo"
// > Object { Firstname: "Joe", Surname: "Schmo", PrettyPrint: PrettyPrint(), Print: Print() }
// We can also add a "Print" method to "nb", and see that it's called by PrettyPrint
nb.Print = function(inputStr) { console.log(inputStr); console.log(this) }
nb.PPrint()
// > undefined undefined
// > Object { PPrint: PrettyPrint(), Print: Print(inputStr) }
// The reason we get "undefined undefined",
// is because "nb" doesn't have a Firstname or Surname field.
// because, again, it's a different "this" context
I personally bind functions in constructor so that their references don't change on each re-render.
This is especially important if you are passing functions to read-only children that you don't need to get updated when their props don't change. I use react-addons-pure-render-mixin for that.
Otherwise, on each parent's re-render, binding will happen, new function reference will get created and passed to children, which is going to think that props have changed.

Stores' change listeners not getting removed on componentWillUnmount?

I am coding a simple app on reactjs-flux and everything works fine except I am receiving a warning from reactjs telling me that I am calling setState on unmounted components.
I have figured out this is because changelisteners to which components are hooked are not being removed from the store on componentWillUnmount. I know it because when I print the list of listeners from Eventemitter I see the listener which was supposed to be destroyed still there, and the list grows larger as I mount/unmount the same component several times.
I paste code from my BaseStore:
import Constants from '../core/Constants';
import {EventEmitter} from 'events';
class BaseStore extends EventEmitter {
// Allow Controller-View to register itself with store
addChangeListener(callback) {
this.on(Constants.CHANGE_EVENT, callback);
}
removeChangeListener(callback) {
this.removeListener(Constants.CHANGE_EVENT, callback);
}
// triggers change listener above, firing controller-view callback
emitChange() {
this.emit(Constants.CHANGE_EVENT);
}
}
export default BaseStore;
I paste the relevant code from a component experiencing this bug (it happens with all components, though):
#AuthenticatedComponent
class ProductsPage extends React.Component {
static propTypes = {
accessToken: PropTypes.string
};
constructor() {
super();
this._productBatch;
this._productBatchesNum;
this._activeProductBatch;
this._productBlacklist;
this._searchById;
this._searchingById;
this.state = this._getStateFromStore();
}
componentDidMount() {
ProductsStore.addChangeListener(this._onChange.bind(this));
}
componentWillUnmount() {
ProductsStore.removeChangeListener(this._onChange.bind(this));
}
_onChange() {
this.setState(this._getStateFromStore());
}
}
This is driving me pretty nuts at this point. Any ideas?
Thank you!
Short version: expect(f.bind(this)).not.toBe(f.bind(this));
Longer explanation:
The cause of the issue is that EventEmitter.removeListener requires that you pass a function you have previously registered with EventEmitter.addListener. If you pass a reference to any other function, it is a silent no-op.
In your code, you are passing this._onChange.bind(this) to addListener. bind returns a new function that is bound to this. You are then discarding the reference to that bound function. Then you try to remove another new function created by a bind call, and it's a no op, since that was never added.
React.createClass auto-binds methods. In ES6, you need to manually bind in your constructor:
#AuthenticatedComponent
class ProductsPage extends React.Component {
static propTypes = {
accessToken: PropTypes.string
};
constructor() {
super();
this._productBatch;
this._productBatchesNum;
this._activeProductBatch;
this._productBlacklist;
this._searchById;
this._searchingById;
this.state = this._getStateFromStore();
// Bind listeners (you can write an autoBind(this);
this._onChange = this._onChange.bind(this);
}
componentDidMount() {
// listener pre-bound into a fixed function reference. Add it
ProductsStore.addChangeListener(this._onChange);
}
componentWillUnmount() {
// Remove same function reference that was added
ProductsStore.removeChangeListener(this._onChange);
}
_onChange() {
this.setState(this._getStateFromStore());
}
There are various ways of simplifying binding - you could use an ES7 #autobind method decorator (e.g. autobind-decorator on npm), or write an autoBind function that you call in the constructor with autoBind(this);.
In ES7, you will (hopefully) be able to use class properties for a more convenient syntax. You can enable this in Babel if you like as part of the stage-1 proposal http://babeljs.io/docs/plugins/transform-class-properties/ . Then, you just declare your event listener methods as class properties rather than methods:
_onChange = () => {
this.setState(this._getStateFromStore());
}
Because the initializer for _onChange is invoked in the context of the constructor, the arrow function auto-binds this to the class instance so you can just pass this._onChange as an event handler without needing to manually bind it.
So I have found the solution, it turns out I only had to assign this._onChange.bind(this) to an internal property before passing it as an argument to removechangelistener and addchangelistener. Here is the solution:
componentDidMount() {
this.changeListener = this._onChange.bind(this);
ProductsStore.addChangeListener(this.changeListener);
this._showProducts();
}
componentWillUnmount() {
ProductsStore.removeChangeListener(this.changeListener);
}
I do not know, however, why this solves the issue. Any ideas?
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 exports component.
I am using the exact same implementation across multiple react components. i.e. this is repeated across several .jsx components.
componentDidMount: function() {
console.log('DidMount- Component 1');
ViewStateStore.addChangeListener(this._onChange);
},
componentWillUnmount: function() {
console.log('DidUnMount- Component 1');
ViewStateStore.removeChangeListener(this._onChange);
},
_onChange:function()
{
console.log('SetState- Component 1');
this.setState(getStateFromStores());
},
Possible Solution
Currently the following is working out for me, but it has been a little temperamental. Wrap the call back in a function/named-function.
ViewStateStore.addChangeListener(function (){this._onChange});
one might also try
ViewStateStore.addChangeListener(function named(){this._onChange});
Theory
EventEmitter is for some reason getting confused identifying the callback to remove. Using a named function is perhaps helping with that.
Try removing the .bind(this) from your addChangeListener and removeChangeListener. They are already bound to your component when they get called.
I decided it so
class Tooltip extends React.Component {
constructor (props) {
super(props);
this.state = {
handleOutsideClick: this.handleOutsideClick.bind(this)
};
}
componentDidMount () {
window.addEventListener('click', this.state.handleOutsideClick);
}
componentWillUnmount () {
window.removeEventListener('click', this.state.handleOutsideClick);
}
}
This is a es6 problem. React.createClass binds 'this' properly for all function defined inside its scope.
For es6, you have to do something yourself to bind the right 'this'. Calling bind(this) however, creates a new function each time, and passing its return value to removeChangeListener won't match the function passed into addChangeListener created by an earlier bind(this) call.
I see one solution here where bind(this) is called once for each function and the return value is saved and re-used later. That'll work fine. A more popular and slightly cleaner solution is using es6's arrow function.
componentDidMount() {
ProductsStore.addChangeListener(() => { this._onChange() });
}
componentWillUnmount() {
ProductsStore.removeChangeListener(() => { this._onChange());
}
Arrow functions capture the 'this' of the enclosing context without creating new functions each time. It's sort of designed for stuff like this.
As you already got to know the solution here, I will try to explain what's happening.
As per ES5 standard, we used to write following code to add and remove listener.
componentWillMount: function() {
BaseStore.addChangeListener("ON_API_SUCCESS", this._updateStore);
},
componentWillUnmount: function() {
BaseStore.removeChangeListener("ON_API_SUCCESS", this._updateStore);
}
In above code, memory reference for the callback function (ie: this._updateStore) is same. So, removeChangeListener will look for reference and will remove it.
Since, ES6 standard lacks autobinding this by default you have to bind this explicitly to the function.
Note: Bind method returns new reference for the callback.
Refer here for more info about bind
This is where problem occurs. When we do this._updateStore.bind(this), bind method returns new reference for that function. So, the reference that you have sent as an argument to addChangeListener is not same as the one in removeChangeListener method.
this._updateStore.bind(this) != this._updateStore.bind(this)
Solution:
There are two ways to solve this problem.
1. Store the event handler (ie: this._updateStore) in constructor as a member variable. (Your solution)
2. Create a custom changeListener function in store that will bind this for you. (Source: here)
Solution 1 explanation:
constructor (props) {
super(props);
/* Here we are binding "this" to _updateStore and storing
that inside _updateStoreHandler member */
this._updateStoreHandler = this._updateStore.bind(this);
/* Now we gonna user _updateStoreHandler's reference for
adding and removing change listener */
this.state = {
data: []
};
}
componentWillMount () {
/* Here we are using member "_updateStoreHandler" to add listener */
BaseStore.addChangeListener("ON_STORE_UPDATE", this._updateStoreHandler);
}
componentWillUnmount () {
/* Here we are using member "_updateStoreHandler" to remove listener */
BaseStore.removeChangeListener("ON_STORE_UPDATE", this._updateStoreHandler);
}
In above code, we are binding this to _updateStore function and assigning that to a member inside constructor. Later we are using that member to add and remove change listener.
Solution 2 explanation:
In this method, we modify BaseStore functionalities. Idea is to modify addChangeListener function in BaseStore to receive second argument this and inside that function we are binding this to the callback and storing that reference, so that while removing change listener we can remove with that reference.
You can find complete code gist here and source here.

Change state when properties change and first mount on React - Missing function?

I have come across a problem about states based on properties.
The scenario
I have a Component parent which creates passes a property to a child component.
The Child component reacts according to the property received.
In React the "only" proper way to change the state of a component is using the functions componentWillMount or componentDidMount and componentWillReceiveProps as far as I've seen (among others, but let's focus on these ones, because getInitialState is just executed once).
My problem/Question
If I receive a new property from the parent and I want to change the state, only the function componentWillReceiveProps will be executed and will allowed me to execute setState. Render does not allow to setStatus.
What if I want to set the state on the beginning and the time it receives a new property?
So I have to set it on getInitialState or componentWillMount/componentDidMount. Then you have to change the state depending on the properties using componentWillReceiveProps.
This is a problem when your state highly depends from your properties, which is almost always. Which can become silly because you have to repeat the states you want to update according to the new property.
My solution
I have created a new method that it's called on componentWillMount and on componentWillReceiveProps. I have not found any method been called after a property has been updated before render and also the first time the Component is mounted. Then there would not be a need to do this silly workaround.
Anyway, here the question: is not there any better option to update the state when a new property is received or changed?
/*...*/
/**
* To be called before mounted and before updating props
* #param props
*/
prepareComponentState: function (props) {
var usedProps = props || this.props;
//set data on state/template
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === usedProps.currentQuestion.id;
});
this.setState({
currentResponses: currentResponses,
activeAnswer: null
});
},
componentWillMount: function () {
this.prepareComponentState();
},
componentWillReceiveProps: function (nextProps) {
this.prepareComponentState(nextProps);
},
/*...*/
I feel a bit stupid, I guess I'm loosing something...
I guess there is another solution to solve this.
And yeah, I already know about this:
https://facebook.github.io/react/tips/props-in-getInitialState-as-anti-pattern.html
I've found that this pattern is usually not very necessary. In the general case (not always), I've found that setting state based on changed properties is a bit of an anti-pattern; instead, simply derive the necessary local state at render time.
render: function() {
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === this.props.currentQuestion.id;
});
return ...; // use currentResponses instead of this.state.currentResponses
}
However, in some cases, it can make sense to cache this data (e.g. maybe calculating it is prohibitively expensive), or you just need to know when the props are set/changed for some other reason. In that case, I would use basically the pattern you've written in your question.
If you really don't like typing it out, you could formalize this new method as a mixin. For example:
var PropsSetOrChangeMixin = {
componentWillMount: function() {
this.onPropsSetOrChange(this.props);
},
componentWillReceiveProps: function(nextProps) {
this.onPropsSetOrChange(nextProps);
}
};
React.createClass({
mixins: [PropsSetOrChangeMixin],
onPropsSetOrChange: function(props) {
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === props.currentQuestion.id;
});
this.setState({
currentResponses: currentResponses,
activeAnswer: null
});
},
// ...
});
Of course, if you're using class-based React components, you'd need to find some alternative solution (e.g. inheritance, or custom JS mixins) since they don't get React-style mixins right now.
(For what it's worth, I think the code is much clearer using the explicit methods; I'd probably write it like this:)
componentWillMount: function () {
this.prepareComponentState(this.props);
},
componentWillReceiveProps: function (nextProps) {
this.prepareComponentState(nextProps);
},
prepareComponentState: function (props) {
//set data on state/template
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === props.currentQuestion.id;
});
this.setState({
currentResponses: currentResponses,
activeAnswer: null
});
},

Resources