React - Cannot read property 'call' of undefined - javascript

Everything seems to work with this small app except adding a new note. Button is located on the Board component.
i know this problem is usually caused by not binding value of 'this' properly. I'm not sure if that's the issue here or if i'm missing something else. Thanks
Demo: http://jsbin.com/pewahi/edit?js,output
/* jshint asi:true */
class Note extends React.Component {
constructor(props) {
super(props)
this.state = { editing: props.editing }
}
render() {
if (this.state.editing) {
return this.renderForm()
} else {
return this.renderDisplay()
}
}
edit() {
this.setState({editing: true})
}
save() {
this.props.changeHandler(this.refs.newText.getDOMNode().value, this.props.index)
this.setState({editing: false})
}
remove() {
this.props.removeHandler(this.props.index)
}
renderDisplay() {
return (
<div className="note">
<p>{this.props.children}</p>
<span>
<button className="btn btn-sm glyphicon glyphicon-pencil" onClick={this.edit.bind(this)}></button>
<button className="btn btn-sm glyphicon glyphicon-trash" onClick={this.remove.bind(this)}></button>
</span>
</div>
)
}
renderForm() {
return (
<div className="note">
<textarea ref="newText" defaultValue={this.props.children} className="form-control"></textarea>
<button onClick={this.save.bind(this)} className="btn btn-success btn-sm"><span className="glyphicon glyphicon-floppy-disk"></span> Save</button>
</div>
)
}
}
Note.propTypes = {
editing: React.PropTypes.bool,
onChange: React.PropTypes.func,
onRemove: React.PropTypes.func
}
Note.defaultProps = { editing: false }
class Board extends React.Component {
constructor(props) {
super(props)
this.state = {
notes: [{note: 'hi', id: this.nextId()}]
}
}
update(newText, i) {
var arr = this.state.notes
arr[i].note = newText
this.setState({notes: arr})
}
remove(i) {
var arr = this.state.notes
arr.splice(i, 1)
this.setState({notes: arr})
}
addNote(text) {
var arr = this.state.notes
arr.push({
id: this.nextId(),
note: text
})
console.log(arr)
this.setState({notes: arr})
}
nextId() {
this.uniqueId = this.uniqueId || 0
return ++this.uniqueId
}
eachNote(note, i) {
return (
<Note key={note.id}
index={i}
changeHandler={this.update.bind(this)}
removeHandler={this.remove.bind(this)}
>{note.note}
</Note>
)
}
render() {
return (
<div className="board">
{this.state.notes.map(this.eachNote, this)}
<button onClick={this.addNote.bind(this, "new note")} className="btn btn-success btn-sm glyphicon glyphicon-plus"></button>
</div>
)
}
}
React.render(
<Board />,
document.getElementById('message-board')
)

Your code is fine. This is likely a bug with JSBin, and how it handles transpilation with Babel. If you add the pragma // noprotect to the top of your code you will see that it works.

I was facing the same error. I was using a base component and I noticed that I had removed componentDidMount method of the base component. And when I call super.componentDidMount in sub component it was giving the error. So I have removed super call and problem solved.

Binding this is something of a hassle with ES6 classes in React. One way is to bind them in your constructor like so;
constructor(props) {
super(props)
this.nextid = this.nextid.bind(this)
this.state = {
notes: [{note: 'hi', id: this.nextId()}]
}
}
Another is to use babel.configure({stage: 0}) and arrow functions.
nextid = () => {}

Related

Passing a function to React Component and then calling it causes "Uncaught TypeError: x is not a function"

I am trying to display a Modal in React and once onClose is called it should close itself. I have a function called showModal():
showModal = () => {
this.setState(prev=>({
modalOpen: !prev.modalOpen
}));
};
And I am binding it in the constructor:
constructor(props) {
super(props);
this.state = {
...
modalOpen: false,
};
this.showModal = this.showModal.bind(this);
}
At some point I am calling my Modal component :
function = () => {
...
return (
<div {...attrs}>
...
<div>
<DocumentModal onClose={this.showModal} show={this.state.modalOpen} document = {documents[0].id} />
<button onClick={() => {this.showModal()}}>{click me} »</button>
</div>
}
</div>
</div>
);
}
And here is the Modal component:
class DocumentModal extends React.Component {
constructor(props) {
super(props);
this.state = {
id: this.props.document,
};
}
...
render() {
const modalStyles = {
modal: {
minWidth: '400px',
padding: '2rem',
}
};
if (!this.props.show) {
return null;
}
return (
<div>
<Modal open={this.props.show} little showCloseIcon={true} styles={modalStyles}>
<div className="item">
...
<button type="button" className="close" aria-label="Close"
onClick={() => {this.props.onClose()}}/>
</div>
</Modal>
</div>
);
}
}
Modal.propTypes = {
onClose: PropTypes.func.isRequired,
show: PropTypes.bool,
document: PropTypes.string,
};
export default DocumentModal;
When clicking on the close button inside the Modal I get the following error:
Uncaught TypeError: n.props.onClose is not a function
at n.onClickCloseIcon (modal.js:66)
at Object.c (react-dom.production.min.js:26)
at Object.invokeGuardedCallback (react-dom.production.min.js:25)
at Object.invokeGuardedCallbackAndCatchFirstError (react-dom.production.min.js:25)
at p (react-dom.production.min.js:30)
at b (react-dom.production.min.js:32)
at y (react-dom.production.min.js:32)
at Array.forEach (<anonymous>)
at v (react-dom.production.min.js:31)
at S (react-dom.production.min.js:34)
It might be worth mentioning that there are multiple items for which the Modal is rendered. For example 5 different items with 5 different ID's but the onClose method is still the same for everyone.
What am I doing wrong?
You have to add onClose in your Modal component
<Modal open={this.props.show} little showCloseIcon={true} styles={modalStyles} onClose={this.props.onModalClose}> //rename your prop function to onModalClose//

Why is this closeModal in React not working?

Currently I have a component which render a modal component.
constructor(props) {
super(props);
this.state = {
fieldId: "",
rating: -1,
description: "",
showModal: false //This is what checks if the modal should or shouldnt be rendered
};
console.log(props) //week number, beginning with 0
this.showModal = this.showModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
showModal() {
console.log("showmodal state before any click")
console.log(this.state.showModal) //false
this.setState({
showModal: true
}, () => {
console.log("clicked show modal")
console.log(this.state.showModal) //true
});
}
closeModal() {
this.setState({
showModal: false
}, () => {
console.log("clicked closeModal")
console.log(this.state.showModal) //true <------ !WHY IS THIS TRUE??
});
}
render() {
var weekId = this.props.weekId;
//current week, round up
//lived weeks, round down
//total weeks, round up
if (weekId < this.props.weeksToRegisterDate) {
return <div id={weekId} className="cube-lived"></div>
} else if (weekId == this.props.currentWeek) {
return <div id={weekId} title={weekId} className="cube green" onClick={this.showModal}>
<CalendarFieldModal show={this.state.showModal} close={this.closeModal} />
</div>
} else {
return <div id={weekId} title={weekId} className="cube white" onClick={this.showModal}>
<CalendarFieldModal show={this.state.showModal} close={this.closeModal} />
</div>
}
}
}
Basically if I click the cube it will render a modal Component, which looks like this.
import React from 'react'
import './CalendarFieldModal.css'
class CalendarFieldModal extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
if (!this.props.show) {
return null;
}
// return <div className="cube-to-spawn"></div>
return <div id="open-modal" class="modal-window">
<div>
<a href="#" title="Close" class="modal-close" onClick={this.props.close}>Close</a> //this triggers the function and should close the modal
<h1>Voilà!</h1>
<div>A CSS-only modal based on the :target pseudo-class. Hope you find it helpful.</div>
<div><small>Check out</small></div>
</div>
</div>
}
}
export default CalendarFieldModal;
Now in the late component I have a close button, which If i click, i can see the closeModal() function triggering and logging the console.log , so I suppose that the state is changing to. But still it doesnt close the modal. So i dont understand the issue
EDIT: This is how it looks https://codesandbox.io/embed/quirky-ardinghelli-8fgtx?fontsize=14&hidenavigation=1&theme=dark you must open the preview in a new windows, or else the modal wont load
Refactor it a bit:
if (weekId < this.props.weeksToRegisterDate) {
return <div id={weekId} className="cube-lived"></div>
} else {
const currentStyle = (weekId == this.props.currentWeek) ? "green" : "white";
return <div id={weekId} title={weekId}
className=`cube ${currentStyle}`
{...(!this.state.showModal && { onClick: this.showModal })}
>
<CalendarFieldModal show={this.state.showModal} close={this.closeModal} />
</div>
}
Explanation
Outer <div/> was not this.state.showModal changes aware - no props change, not rerendered - no matter inner object's props changed and should be rerendered.

why setState does'nt change the "editing" key to true?

The edit function does not change the editing in state to true.
I don't know what to do:
class Note extends React.Component {
constructor (props) {
super(props)
this.state = {
editing: false
}
}
/**edit() {
this.setState = ({
editing: true
})**/
}
remove() {
alert ("removed")
}
save() {
alert ("saved")
}
renderForm() {
return (
<div className="note">
<form>
<textarea/>
<button onClick={this.save}> click </button>
</form>
</div>
)
}
renderDisplay() {
return (
<div className="note">
<p> Learn React </p>
<span>
<button onClick={this.edit} id="edit"> Edit </button>
<button onClick={this.remove} id="remove"> Delete </button>
</span>
</div>
)
}
render() {
return this.state.editing ? this.renderForm() : this.renderDisplay()
}
}
You should change this:
this.setState = ({
editing: true
})
to this:
this.setState({
editing: true
})
Also, you should bind edit method or use an arrow function instead. It's required because in JavaScript function context is defined while calling the function, not while defining it.
Here you can check an example.

TypeError: Cannot read property 'edit' of undefined

I keep getting the following 2 errors for my buttons:
TypeError: Cannot read property 'edit' of undefined
TypeError: Cannot read property 'remove' of undefined
I am building a todo list, each note has 2 buttons 'add' and 'Remove'.
I managed to get the note buttons working when I call DisplayNote once.
Whenever I try to make multiple notes with JS map the buttons stop working and I can't figure out why its not working now. Code is attached.
todo list image
import React from 'react';
class DisplayNote extends React.Component {
handleEdit(e) {
console.log('sdfsdfdfs');
this.props.edit(e)
}
handleRemove(e) {
console.log('sdfsdfdfs');
this.props.remove(e)
}
render(){
return(
<div className="note">
<p>{this.props.note}</p>
<span>
<button onClick={this.handleEdit.bind(this)}>Edit</button>
</span>
<span>
<button onClick={this.handleRemove.bind(this)}>Remove</button>
</span>
</div>
);
}
}
class EditNote extends React.Component {
handleSave(e) {
var val = this.refs.newText.value;
this.props.saveNote(val)
}
render(){
return (
<div className="note">
<textarea ref="newText" defaultValue="test">
</textarea>
<button onClick={this.handleSave.bind(this)}>Save</button>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.edit = this.edit.bind(this);
this.saveNote = this.saveNote.bind(this);
this.remove = this.remove.bind(this);
this.state = {
editing: false,
notes: ['Call Tim','sdsdsd', 'dentist', 'Email Julie']
}
}
AppObject = {
count: 1,
price: 15.00,
amount: '12'
}
AppArray = ['tim','ali', 'jim', 'tom']
edit(e) {
this.setState({editing: true});
console.log('AppObject', this.AppObject);
}
saveNote(val) {
this.setState({editing: false});
console.log('Save note value ' + val)
}
remove() {
alert('remove');
console.log('AppArray', this.AppArray);
}
eachNote(note, i) {
return(
<DisplayNote key={i}
note={note}
edit={(e) => this.edit(e)}
remove={(e) => this.remove(e)}>
{note}
</DisplayNote>
);
}
render() {
if(this.state.editing) {
return (
<div>
<EditNote saveNote={(e) => this.saveNote(e)} />
<div>{this.props.count}</div>
</div>
);
}else{
return (
<div>
/* Calling it once*/<DisplayNote edit={(e) => this.edit(e)} remove={(e) => this.remove(e)} />
<div>{this.props.count}</div>
<div>
/* Using map to create multiple notes */{this.state.notes.map(this.eachNote)}
</div>
</div>
);
}
}
}
App.propTypes = {
count: function(props, propName){
if(typeof props[propName] !== 'number'){
return new Error('Count prop must be a number');
}
if(props[propName] > 100){
return new Error('Creating ' + props[propName] + ' notes is too much!');
}
}
}
export default App;
I think you are loosing the context inside map function, you need to define the binding for that also.
Use this line in the constructor, it will bind that function:
this.eachNote = this.eachNote.bind(this);
Or use that function like this:
{this.state.notes.map((note, i) => this.eachNote(note,i)}
Or
{this.state.notes.map(this.eachNote)}
eachNote = (note, i) => { //use arrow function here
}

this.someFunction is not a function

After having read about the bind requirement for methods to be bound to a React ES6 class, I am still having some difficulty with this example:
class ProductList extends React.Component {
constructor(props) {
super(props);
this.state = { products: [] };
this.updateState = this.updateState.bind(this);
}
componentDidMount() {
this.updateState();
}
handleProductUpvote(productId) {
Data.forEach((item) => {
if (item.id === productId) {
item.votes = item.votes + 1;
return;
}
});
this.updateState();
}
updateState() {
const products = Data.sort((a,b) => {
return b.votes - a.votes;
});
this.setState({ products });
}
render() {
const products = this.state.products.map((product) => {
return (
<Product
key={'product-' + product.id}
id={product.id}
title={product.title}
description={product.description}
url={product.url}
votes={product.votes}
submitter_avatar_url={product.submitter_avatar_url}
product_image_url={product.product_image_url}
onVote={this.handleProductUpvote}
/>
);
});
return (
<div className='ui items'>
{products}
</div>
);
}
}
class Product extends React.Component {
constructor() {
super();
this.handleUpvote = this.handleUpvote.bind(this);
}
handleUpvote() {
this.props.onVote(this.props.id);
}
render() {
return (
<div className='item'>
<div className='image'>
<img src={this.props.product_image_url} />
</div>
<div className='middle aligned content'>
<div className='header'>
<a onClick={this.handleUpvote}>
<i className='large caret up icon'></i>
</a>
{this.props.votes}
</div>
<div className='description'>
<a href={this.props.url}>
{this.props.title}
</a>
</div>
<div className='extra'>
<span>Submitted by:</span>
<img
className='ui avatar image'
src={this.props.submitter_avatar_url}
/>
</div>
</div>
</div>
);
}
}
ReactDOM.render(
<ProductList />,
document.getElementById('content')
);
This returns
Uncaught TypeError: this.updateState is not a function(...) at handleProductUpvote
Is the initialized binding not sufficient in this case?
Whenever you see this issue, you don't want to be adding the bind to the method that it's trying to call right then, but the method that you are inside of when the "this.xxx not defined" issue occurs.
Currently, it's getting the function handleProductUpvote just fine - but it's calling it in the wrong object context. So you need to do the same thing as you did with updateState in the constructor, but with that function. Though I have limited react knowledge I believe it's common to do that for every function that's used as an event listener or callback.

Categories

Resources