I am rendering a list of items retrieved from Firebase. For each item, I render a div, that includes a button that removes said item.
Relevant code:
constructor(props){
// Pass props to parent class
super(props);
this.removeItem = this.removeItem.bind(this);
this.getList = this.getList.bind(this);
...
}
removeItem(key) {
this.ref(key).remove()
}
getList() {
var list = []
this.ref.on("value", function (snapshot) {
for (var key in snapshot.val()) {
list.push(<div class="todo-item">{snapshot.val()[key]} <button onClick={() => this.removeItem(key)}> X </button> </div>)
}
}, function (error) {
console.log("Error: " + error.code);
});
return(list)
}
render() {
return (
<div class="todolist">
.....
{this.getList()}
</div>
)
}
The list of items and their remove buttons renders fine. However, when clicking the remove button, I get a TypeError: Cannot read property 'removeItem' of null
As removeItem is a function of this, I assume that this is not properly bound and thus null.
However, I bound both functions removeItem and getList in the constructor.
Does someone know where I am going wrong?
This is the most common problem of context being lost when this is accessed from anonymous function.
To get around this,
Use arrow functions:
this.ref.on("value", (snapshot) => { ... }, (error) => { ... });
OR
Use bind(this)
this.ref.on("value", function (snapshot) { ... }.bind(this), function (error) { ... }.bind(this));
Related
Getting this is undefined error. Tried searching for solution but didn't find any.
This same code is working fine on another page but don't know what's wrong here. Printing output of brands before setState is printing valid result.
class FindSponsors extends Component {
constructor() {
super();
this.state = {
data: []
}
}
componentDidMount() {
db.ref("sponsor").once("value").then(function (snapshot) {
let data = snapshot.val()
let brands = []
for (let brand in data) {
brands.push({
name: data[brand].Name,
website: data[brand].Website
})
}
this.setState({ //Error here
data: brands
})
});
}
render() {
return (
<div className="main">
</div>
);
}
}
export default FindSponsors;
Try using the arrow function:
db.ref("sponsor").once("value").then((snapshot) => { /*your function body here*/})
this keyword is nothing more than a link to an execution context. In your example above you "execute" function "in the callback".
Try reading this to understand better how it works and how arrows functions are different from regular ones.
The problem here is that when you define your callback function in the .then, the context of this function changes and this will basically refer to the inside of the function.
Two solutions here:
Use the .bind method to change the value of this for your callback. It would be something like this:
const callback = function (snapshot) {
// Your callback logic
}
db.ref("sponsor").once("value").then(callback.bind(this));
Use an arrow function. They have the specificity to not have their own bindings of the this keyword. This would be like this:
db.ref("sponsor").once("value").then((snapshot) => {
// Your callback logic
});
I am saving some data (3 documents) into state.
I am receiving 'Uncaught TypeError: Cannot read property 'documents' of undefined' error when trying to map over an array in my react app.
Can anybody see where I am going wrong? 3 items should be mapped in the render function.
export default class Kim extends React.Component<IKimProps, IKimState> {
public constructor(props) {
super(props);
this.state = {
documents: []
}
//SPComponentLoader.loadCss('//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css');
}
componentDidMount() {
axios
.get("https://hiddenforsecurityreasons.com/sites/KIM/_api/lists/getbytitle('MyList')/items?$select=Id,Title,File/ServerRelativeUrl&$expand=File",
{ params:{},
headers: { 'accept': 'application/json;odata=verbose' }
})
.then(response => {
this.setState({
documents: response.data
})
})
.catch(error => {
console.log(error);
});
}
public render(): React.ReactElement<IKimProps> {
return (
<div className={ styles.kim }>
{ setTimeout(function(){
this.state.documents.d.results.map(document => {
<li>{document.Title}</li>
})
}, 6000) }
</div>
);
}
}
Thanks for looking!
When inside the timeout function, this is a reference to that function scope, not your React class scope, so this.state inside the timeout function is actually undefined because there's no state inside the function, so when your code tries to read documents, it can't because you cannot get documents from undefined, as it states.
You can remove your timeout and add an if check around your map call instead.
public render(): React.ReactElement<IKimProps> {
return (
<div className={ styles.kim }>
{this.state.documents.length? this.state.documents.d.results.map(document => <li>{document.Title}</li> ) : null }
</div>
);
}
Another way of dealing with this while keeping your timeout function is to use arrow functions.
While in ES5 ‘this’ referred to the parent of the function, in ES6,
arrow functions use lexical scoping — ‘this’ refers to it’s current
surrounding scope and no further. read more here
try arrow function, for sure it is a problem with this
{ setTimeout(() => {
this.state.documents.d.results.map(document => {
<li>{document.Title}</li>
})
}, 6000) }
I am writing a React class and trying to call a function form another function but in a for loop, I am getting this error
Uncaught ReferenceError: isValidated is not defined
If I move my function call out of for each loop, it work fine. I am using arrow function, so I believe I don;t need binding in constructor.
class ResourceManagerForm extends React.Component {
render() {
return (<button className="ms-Button ms-Button--primary" onClick={this.onSaveButtonClick}>
<span className="ms-Button-label">Create Account name</span>
</button>)
}
isValidated = (control) =>
{
//some code here
}
onSaveButtonClick = (e) =>
{
this.isValidated(null); //works fine here
$("#resource-manager [id]").each(function (index, value)
{
if(isValidated(value)) //does not work
{
}
if(this.isValidated(value)) //also does not work
{
}
});
}
}
It's because of how you define the anonymous function in .each()
For this to be bound inside the function, you need to declare it as an "arrow" function i.e. (index, value) => { ... } instead of function(index, value) { ... }:
this.isValidated(null); //works fine here
$("#resource-manager [id]").each((index, value) => {
if(this.isValidated(value)) { // this is bound inside the function now
}
});
This blog post explains how arrow functions behave.
I am new in react. For my project i want to change my state by clicking an icon from table which will change my state. I am using getCellActions (react-data-grid). How could I pass my custom function alone with column and row object.
Thanks in advance.
NT: I am not using redux
getCellActions(column, row, state) {
if(column.key === 'modify'){
return [
{
icon: 'fa fa-edit',
callback: () => {
console.log(row.id);
//this.getRoleData();
}
}
];
}
}
You can simply use ES6 and pass an arrow function expression that simply invokes the function you want and binds the function with this automatically..
In react-data-grid, you would have to do something like below:
<ReactDataGrid
columns={this._columns}
rowGetter={this.rowGetter}
rowsCount={this._rows.length}
minHeight={300}
getCellActions= {(column, row) => this.getCellActions(column, row)}
state = {this.state}
/>
And your getCellActions function can be as it is:
getCellActions(column, row) {
if(column.key === 'modify'){
return [
{
icon: 'fa fa-edit',
callback: () => this.getRoleData(row.id)
}
];
}
}
The thing is that when you write:
getCellActions= {(column,row) => this.getCellActions(column, row)}
you actually pass an anonymous function which will only be executed when triggered.
I would like to add that when you write an arrow function in one line, it automatically returns that statement, except you wrap that line in curly braces and write a normal function. So, the above line works as :
getCellActions= {(column,row) => return this.getCellActions(column, row)}
So, the MANTRA is: return the reference, do not execute it before the trigger.
The problem why this.getRoleData() is not working, is because this inside of your callback is not in the context of your component.
Change your getCellActions function to an arrow function, so you have the component as context (inside a arrow function this keeps its meaning from its original context):
import React from 'react';
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedRowId: null
};
}
myCallback = (rowId) => {
console.log(rowId);
this.setState({ selectedRowId: rowId });
};
getCellActions = (column, row, state) => {
if(column.key === 'modify'){
return [
{
icon: 'fa fa-edit',
callback: () => {
console.log(row.id);
this.myCallback(row.id);
}
}
];
}
}
render() {
// your render function...
}
}
Alternative:
Bind this to getCellActions in the constructor like:
this.getCellActions = this.getCellActions.bind(this);
I am trying to change the state on the click of a button I created through DOM methods. I tried passing "this" as a variable through the arguments of the function
var self="this"
b.addEventListener("click", function(self){
self.setState({health:100}) })
and also tried adding .bind(this) at the end of the function but no luck.
b.addEventListener("click", function(){
this.setState({health:100}) })
This issue can be handled using right declaration and definition of self.
OR with the help of arrow function(implicit Lexical scope)
componentDidMount(){
//assuing b defined
//1. if you want to use self
const self = this; // this should not be double quoted;
b.addEventListener("click", function () {
self.setState({ health: 100 });
}
// 2. using arrow function ( implicit lexical scope)
b.addEventListener("click", ()=> {
this.setState({ health: 100 });
}
}
You should do a separate function that manages the event. And then bind the function to the constructor. For example
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
Also, take in count this :
When using React you should generally not need to call addEventListener to add listeners to a DOM element after it is created. Instead, just provide a listener when the element is initially rendered.
Handling Events in react
use:
var self=this
b.addEventListener("click", function(){
self.setState({health:100}) })
Remove double quotes from this, it should be working.
No need const self= this;(optional)
simply use arrow function.this work perfectly
b.addEventListener("click", () => {
this.setState({ health: 100 });
}