I have a class in which I bind some functions in the constructor. This works fine and as expected
class Row {
constructor(props) {
super(props)
this.onEditRowClick = this.onEditRowClick.bind(this)
this.onCommitRowClick = this.onCommitRowClick.bind(this)
this.onDeleteRowClick = this.onDeleteRowClick.bind(this)
this.onCellChange = this.onCellChange.bind(this)
}
...
}
however, if I change to
class Row {
constructor(props) {
super(props)
let handlers = [this.onEditRowClick, this.onCommitRowClick, this.onCellChange, this.onDeleteRowClick]
handlers.forEach(handler => {handler = handler.bind(this)})
}
...
}
it is clearly not working as I get exceptions indicating that this in my function invocations is null.
I thought arrow functions implemented a lexical this binding?
Also, if I do this
class Row {
constructor(props) {
super(props)
[this.onEditRowClick, this.onCommitRowClick, this.onCellChange, this.onDeleteRowClick].forEach(handler => {handler = handler.bind(this)})
}
}
I get
Uncaught TypeError: Cannot read property 'forEach' of undefined
while this is totally fine
[1,2,3].forEach(function(item){console.log(item)})
Maybe Im missing something very obvious and it's time for me to go to bed?
Function.prototype.bind() creates new function from the existing one which is bound to the passed context. Therefore you reassign properties in your first working example:
this.onEditRowClick = this.onEditRowClick.bind(this);
However in your latter example you skipped the reassign phase.
To solve this you can iterate over method names, bind it to the this instance and reassign:
class Row {
constructor(props) {
super(props);
let handlers = [
'onEditRowClick',
'onCommitRowClick',
'onCellChange',
'onDeleteRowClick'
];
handlers.forEach(handler => {
this[handler] = this[handler].bind(this);
});
}
...
}
Related
I have Cannot read property 'convertIntoCorrectDate' of undefined error.
I think when I call this.convertIntoCorrectDate, this refers to objectResa and not my component.
I tried to call myComp.convertIntoCorrectDate instead but it triggers error too (myComp.convertIntoCorrectDate is not a function).
Do you know how I can call my function ?
class myComp extends Component{
constructor(props){
// this.convertIntoCorrectDate = this.convertIntoCorrectDate.bind(this);
}
fillBooking(responseJson){
var reservations = responseJson.reservations;
reservations.forEach(function(objectResa) {
var aResaEvent = {
id: objectResa.resa.ID,
start: this.convertIntoCorrectDate(objectResa.resa.start),
end: this.convertIntoCorrectDate(objectResa.resa.end)
};
console.log(aResaEvent);
});
}
convertIntoCorrectDate(date){
// code to be written
return "0";
}
```
If you change your fillBooking(responseJson) function to:
fillBooking = (responseJson) => {
and the forEach to:
reservations.forEach((objectResa) => {
(to arrow functions), the this should point to the scope you desire.
I am getting above error while setting the data in state in reactjs,
Scenario: passing data from child component to parent component, in child component I am calling parent function and changing the state value using setstate,
ChildComponent
search(){
var input = this.refs.userInput.value;
this.props.findSearch(input);
input.value = '';
}
Parent Component:
findSearch(input){
axios.get(`http://localhost:3000/blogs`)
.then(res => {
input = input.toLowerCase();
let rest = res.data.filter((e)=>{
e.cardtitle === input;
});
this.setState({result:rest}); // here I am getting the error
})
}
can you please help me out or other alternatives.
Bind function to this context in your react component.
constructor(props) {
super(props);
this.findSearch = this.findSearch.bind(this);
}
Seems like a this context related issue.
You should bind your function to the class with this or just use an arrow function as a class field to get a lexical context for this:
findSearch = (input) => {
axios.get(`http://localhost:3000/blogs`)
.then(res => {
input = input.toLowerCase();
let rest = res.data.filter((e)=>{
e.cardtitle === input;
});
this.setState({result:rest}); // here I am getting the error
})
}
Note that class fields are a proposal in stage 3 and you would likely need to add babel-plugin-transform-class-properties
The first time my array is rendered it is in the correct order, however, if it is changed the rendered order remains the same.
For example:
construct() {
this.state = {
test_array: [1,2,3,4]
}
let self = this;
setTimeout(function(){
self.scramble();
}, 5000);
}
scramble() {
this.state.test_array = [3,1,2,4];
this.setState(self.state);
}
render() {
this.state.test_array.forEach(function(item){
console.log(item);
});
return (
<div>
{this.state.test_array}
</div>
);
}
Results in:
On the console (the current order, correct):
3
1
2
4
Rendered as DOM (the original order, incorrect):
1
2
3
4
Any idea why this is failing to render in the correct order?
You were very close. Here's a few things I changed to fix it:
construct should be constructor
You always need to call super() as the first line of a constructor. (You don't really need to worry about this, it's an Object Oriented thing; google it if you're curious)
Use "arrow functions" instead of "keyword functions" or .bind(this) to prevent this from changing contexts
Do not modify this.state; always call this.setState if you want it to change
class OrderThing extends React.Component {
constructor() {
super()
this.state = {
test_array: [1,2,3,4]
}
setTimeout(() => {
this.scramble();
}, 5000);
}
scramble() {
this.setState({
test_array: [3,1,2,4]
});
}
render() {
this.state.test_array.forEach(function(item){
console.log(item);
});
return (
<div>
{this.state.test_array}
</div>
);
}
}
const div = document.createElement('div')
document.body.appendChild(div)
ReactDOM.render(<OrderThing />, div)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
A few suggestions here.
First of all, there is no construct() in js, but there is constructor().
Secondly, you should always call super method with props as an argument in constructor, like this:
constructor(props) {
super(props);
...
}
Finally, react developers highly recommend to modify state only using setState() method.
So you should rewrite your scramble method.
scramble() {
this.setState({test_array: [3,1,2,4]});
}
This changes should help you a little bit.
I'm new to react.js and I'm trying to math.random a number and put it in this.props.firstCard property, but it returns undefined. I tried some variables but only result is undefined or syntax error. Code:
handleClick() {
let firstCard = this.state.isFirstCardChosen;
function chooseCard() {
return Math.floor(Math.random() * (80 - 1) + 1);
}
if (firstCard == false) {
this.props.firstCard = chooseCard;
console.log(this.props.firstCard);
}
}
What's wrong with this one? Thanks in advance.
I'm going to hazard a guess and suggest that
if:
the problem isn't just one caused by the fact you are trying to mutate props (props are immutable).
and:
You're using ES2015 class syntax
then
the problem may be that your handleClick method isn't properly bound to the component state so the call to this.props returns undefined.
To solve this you can bind the handleClick in the constructor as follows:
constructor (props){
super(props);
this.handleClick = this.handleClick.bind(this);
}
As everyone else has said the props is immutable. Also make sure you are setting props correctly and binding your handleClick function in your constructor i.e:
constructor(props) {
super(props);
his.handleClick = this.handleClick.bind(this);
}
I am trying to use .bind() when using a method in my component.
The reason is simple: In a loop I am returing Components and extend them with a property which is calling a method. But for every loop-item this I want to extend the this Object with some information (like a key).
Example:
Items.jsx
Items = React.createClass({
eventMethod() {
console.log('this event was triggered by key:', this.key);
},
items() {
let items = [];
let properties = {};
_.each(this.props.items, (itemData, key)=>{
properties.eventMethodInItem = this.eventMethod.bind(_.extend(this, {
key
}));
let {...props} = properties;
let item = <Item {...props} key={key} />;
items.push(item);
});
return items;
},
render() {
return(<div>{this.items()}</div>);
}
});
Item.jsx
Item = React.createClass(...);
In this case (and its working) when the Item Component is triggering the prop "eventMethodInItem" my method "eventMethod" will be called and this.key has the correct value!
So - whats now the question ? Its working perfect, right ?
Yes.
But ReactJS does not want me to do this. This is what ReactJS is telling me as a console log.
Warning: bind(): You are binding a component method to the component. React does this for you automatically in a high-performance way, so you can safely remove this call. See Items
Maybe you think its a "bad" way to add children to the component like I am doing it but in my special case I need to do this in this way - so I need to bind new information to a method.
I'm not going to pretend that I understand what you are trying to do here, but maybe I can help clear it up anyway.
React takes all of the top level methods found on each component and automagically binds them to the context of the component.
This prevents other methods from overriding the context of this and as a result, if you try to rebind the method, React says "Hey don't bother. I already got it" — which is the warning you are seeing.
Assuming that you really want do this (each time you are mutating the outer properties object by overriding the eventMethodInItem property).
properties.eventMethodInItem = this.eventMethod.bind(_.extend(this, {
key
}));
Then I can't see any reason that the eventMethod has to live on the component, rather than just in the scope of the items function.
items() {
const eventMethod = function() {
console.log('this event was triggered by key:', this.key);
}
// ...
_.each(this.props.items, (itemData, key)=>{
properties.eventMethodInItem = eventMethod.bind(_.extend(this, {
key
}));
// ...
});
},
That way you don't have to fight React to get your program to work.
React is already autobinding this when using React.createClass http://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html#under-the-hood-autobinding-and-event-delegation
Change your binding to
properties.eventMethodInItem = this.eventMethod.bind(null,key);
and your eventMethod to
eventMethod(key) {
console.log('this event was triggered by key:', key);
}
I also suggest using _.map instead of _.each
items() {
return _.map(this.props.items, (itemData, key) => {
return <Item
handleEventMethod={this.eventMethod.bind(null,key)}
key={key} />;
});
},
Good pattern
https://www.newmediacampaigns.com/blog/refactoring-react-components-to-es6-classes
Before :
class ExampleComponent extends React.Component {
constructor() {
super();
this. _handleClick = this. _handleClick.bind(this);
this. _handleFoo = this. _handleFoo.bind(this);
}
// ...
}
After :
class BaseComponent extends React.Component {
_bind(...methods) {
methods.forEach( (method) => this[method] = this[method].bind(this) );
}
}
class ExampleComponent extends BaseComponent {
constructor() {
super();
this._bind('_handleClick', '_handleFoo');
}
// ...
}
another good hacks for this topic http://egorsmirnov.me/2015/08/16/react-and-es6-part3.html