State is undefined in scope of map - reactjs

I am new to reactjs and trying to learn the concepts. I am creating demo sticky notes app on reactjs. I am getting error
index.js: Uncaught TypeError: Cannot read property 'notes' of undefined
I have Board component and Notes components as follows.
Notes Component:
import React from 'react';
import ReactDOM from 'react-dom';
class Note extends React.Component {
constructor() {
super();
this.state = {editing: false}
this.edit = this.edit.bind(this);
this.save = this.save.bind(this);
}
edit() {
this.setState({editing: true});
}
save() {
this.props.onChange(ReactDOM.findDOMNode(this).value, this.props.index);
this.setState({editing: false});
}
remove() {
this.props.onRemove(this.props.index);
}
renderDisplay() {
return (
<div className='note'>
<p>{this.props.children}</p>
<span>
<button onClick={this.edit} className='btn btn-primary glyphicon glyphicon-pencil'/>
<button onClick={this.remove} className='btn btn-danger glyphicon glyphicon-trash'/>
</span>
</div>
);
}
renderForm() {
return (
<div className='note'>
<textarea className='form-control' defaultValue={this.props.children} ref='newText'></textarea>
<button className='btn btn-success btn-sm glyphicon glyphicon-floppy-disk' onClick={this.save} />
</div>
);
}
render() {
if(this.state.editing) {
return this.renderForm();
} else {
return this.renderDisplay();
}
}
}
export default Note;
And Board Component:
import React from 'react';
import ReactDOM from 'react-dom';
import Note from './Note';
class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
notes: [
'Check emails',
'Log in to jira',
'Fix the issues',
'Logout the system'
]
}
}
update(newText, i) {
console.log(this);
console.log(this.state);
var arr = this.state.notes;
arr[i] = newText;
this.setState({notes: arr});
}
remove(i) {
var arr = this.state.notes;
arr.splice(i, 1);
this.setState({notes: arr});
}
eachNote(note, i) {
return (
<Note key={i}
index={i}
onChange={this.update}
onRemove={this.remove}
>{note}</Note>
);
}
render() {
return (
<div className='board'>
{this.state.notes.map(this.eachNote, this)}
</div>
);
}
}
ReactDOM.render(<Board></Board>, document.getElementById('react-container'));
export default Board;
I am getting error when i try to update a note, as the note is render using the state notes variable and i have attached a property on each note as onChange: {this.update}. In update i am accessing state variable notes but state is undefined.
Can anyone give me suggestions about it. Thanks

Try:
onChange={this.update.bind(this)}
Instead of:
onChange={this.update}

For better performance, React recommends binding event handlers in the constructor so they are only bound once for every instance.
Read more here: https://facebook.github.io/react/docs/reusable-components.html#no-autobinding

Related

TypeError: Cannot read properties of undefined (reading 'state')

I tried console.log(this.state.bike). It gave me an array that basically contains everything I need. But when I try to access them I get undefined. How do I access the bikeLocation and bikeDescription? I have added 3 files. BikeServices, ListBikes and ViewBike Components.
import React, { Component } from 'react';
import BikeServices from '../services/BikeServices';
class viewBike extends Component {
constructor(props){
super(props)
this.state = {
bikeId: this.props.match.params.bikeId,
bike: {}
}
}
componentDidMount(){
BikeServices.getBikesById(this.state.bikeId).then( (res) => {
this.setState({bike: res.data});
//console.log(this.state.bike);
console.log(this.state.bike.bikeLocation);
});
}
render() {
return (
<div>
<br></br>
{this.state.bikeId}
<br></br>
{this.state.bike.bikeLocation}
</div>
);
}
}
export default viewBike;
//BikeServices
import axios from 'axios';
const BIKE_URL = `http://localhost:8090/api/v1/bikes`;
class BikeService{
getBikes(){
return axios.get(BIKE_URL);
}
getBikesById(bikeId){
return axios.get(BIKE_URL+ '/' +bikeId);
}
}
export default new BikeService();
//ListBikes component
import React, { Component } from 'react';
import BikeService from '../services/BikeServices'
class ListBikes extends Component {
constructor(props) {
super(props)
this.state = {
bikes : [],
}
this.viewBike = this.viewBike.bind(this);
}
// bikes=[]
componentDidMount(){
BikeService.getBikes().then((res) => {
this.setState({ bikes: res.data});
});
}
viewBike(bikeId){
console.log(this.props)
this.props.history.push(`/bike/${bikeId}`);
}
render() {
return (
<div className='container'>
{
this.state.bikes.map(
bike=>
<div className="card">
<div className="card-header">
<img src="https://c0.wallpaperflare.com/preview/483/210/436/car-green-4x4-jeep.jpg" alt="rover" />
</div>
<div className="card-body">
<span className="tag tag-teal">{bike.bikeStatus}</span>
<h4>
Bike Category: {bike.bikeCategory}
</h4>
<p>
Bike Description: {bike.bikeDescription}
<br></br>
Location:
{bike.location.address}, {bike.location.city}, {bike.location.state}, {bike.location.zip}
</p>
<button style={{marginLeft: "10px"}} onClick={() => this.viewBike(bike.bikeId)} className='btn btn-primary'>View</button>
<button>Rent</button>
</div>
</div>
)
}
</div>
);
}
}
export default ListBikes;
I tried console.log(this.state.bike). It gave me an array that basically contains everything I need. But when I try to access them I get undefined. How do I access the bikeLocation and bikeDescription? I have added 3 files. BikeServices, ListBikes and ViewBike Components.

React create element again on re-render

I want a comment to be posted one after another but not exactly sure how to implement it. Right now, the new comment is replacing the old one. After reading some posts, I think this.props.children could be the answer but stil somewhat confused, so please let me know the best way to implement what I want to. Thanks.
Comment.js:
import React from 'react';
import { Display } from './Display';
export default class Comment extends React.Component {
constructor(props) {
super(props);
this.state = { buttonClicked: false, count: 0 };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({ buttonClicked: true , count : this.state.count + 1 });
console.log('Button clicked');
}
render() {
const comment_form =(
<div className="posts" >
<input type="text" id="comment-box" name="comment" placeholder="Say something nice." />
<button className="submit-button" type="button" onClick={this.handleClick}>Comment</button>
</div>
);
if (this.state.buttonClicked) {
return (
<div>
{comment_form}
<Display commentNumber={this.state.count} />
</div>
);
} else {
return (<div> {comment_form} </div>);
}
}
}
Display.js:
import React from 'react';
import './App.css';
export class Display extends React.Component {
constructor(props){
super(props);
}
render() {
console.log('display rendered');
const comments = (
<div className="display-comments">
<p>Comment {this.props.commentNumber} :{ document.getElementById('comment-box').value }</p>
</div>
);
return (
<div>
{comments}
</div>
);
}
}
Use the map function to dynamically render the elements.
Link: https://reactjs.org/docs/lists-and-keys.html

react js variable can not shown in alert

I'm learning react.js and dealing with the below code:
import React, { Component } from 'react';
class Counter extends Component {
state = {
counts:1,
tags:['tag1', 'tag2', 'tag3']
};
incrementIt() {
this.state.counts++;
console.log("counts value is ", this.state.counts);
}
btnClick(){
this.incrementIt = this.incrementIt.bind(this);
return (
<div>
<button onClick={this.incrementIt} className="btn btn-primary btn-sm">Click me</button>
</div>
)
}
render(){
return(
<div>
{this.btnClick()}
</div>
)
}
}
export default Counter;
I'm expecting counts shows in the alert window but not working, while it works when switch back to console.log(). How comes?
You want to clean up some of that code: use setState, don't directly manipulate state. Also, assign that state in your constructor; don't use bind; and remember that if you want to see state changes, you need to wait until the state actually updates (which you can do with a function as second argument to setState)
import React, { Component } from 'react';
class Counter extends Component {
constructor(props) {
super(props);
this.state = {
counts: 1
};
}
increment(evt) {
let curCount = this.state.count;
this.setState({ counts: curCount + 1}, () => {
console.log(`after incrementing, counts value is ${this.state.counts}.`);
});
}
render(){
return (
<div>
<button onClick={evt => this.increment(evt)} className="btn btn-primary btn-sm">Click me</button>
</div>
)
}
}
export default Counter;

How to retrieve state from a component

I have a Modal dialog component that takes another component as content and returns a promise for handling the result (Idea is from here). How can I extract the 'time' state from the content in order to add it to the promise chain?
import React from 'react'
import Modal from './Modal'
class Late extends React.Component {
constructor(props) {
super(props);
this.state = {
time: '18:48'
};
}
render() {
return (
<div className='modal-body'>
{this.time}
</div>
);
}
}
export default function(message, options) {
var form = <Late description={options.description} />;
return Modal(form, message, options).then(() => ??);
}
import React from 'react'
import ReactDOM from 'react-dom'
import '../css/modal.css'
import Promise from 'bluebird'
import _ from 'lodash'
Promise.config({ cancellation: true });
class Modal extends React.Component {
constructor(props) {
super(props);
this.resolve = null;
}
abort = () => this.promise.cancel();
confirm = () => this.resolve();
componentDidMount() {
this.promise = new Promise(resolve => this.resolve = resolve);
return ReactDOM.findDOMNode(this.refs.confirm).focus();
}
backdrop = () => <div className='modal-backdrop in' />;
modal() {
var style = {display: 'block'};
return (
<div
className='modal in'
tabIndex='-1'
role='dialog'
aria-hidden='false'
ref='modal'
style={style}
>
<div className='modal-dialog'>
<div className='modal-content'>
<div className='modal-header'>
<h4 className='modal-title'>
{this.props.message}
</h4>
</div>
{this.props.children}
<div className='modal-footer'>
<div className='text-right'>
<button type='button' className='btn btn-default' onClick={this.abort} >
{this.props.abortLabel}
</button>
{' '}
<button type='button' className='btn btn-primary' ref='confirm' onClick={this.confirm} >
{this.props.confirmLabel}
</button>
</div>
</div>
</div>
</div>
</div>);
}
render() {
return (
<div>
{this.backdrop()}
{this.modal()}
</div>
);
}
}
export default function(content, message, options) {
var cleanup, component, props, wrapper;
if (options == null) {
options = {};
}
props = _.assign({
message: message
}, options);
wrapper = document.body.appendChild(document.createElement('div'));
component = ReactDOM.render(<Modal {...props}>{content}</Modal>, wrapper);
cleanup = function() {
ReactDOM.unmountComponentAtNode(wrapper);
return setTimeout(function() {
return wrapper.remove();
});
};
return component.promise.finally(cleanup);
};
if i understood you correctly, you wish to make some action once the Modal is done(pressed OK i.e). If so ,than you could wrap the Modal with a component and pass a function as a prop to the Modal so that once the Modal component is handling the wished action it can omit the function from prop.
take a look how to pass a function as a prop, and Lift up the state

cannot read of props null in react js

when trying to click the delete button the error is displayed stating that cannot read props of null and try to bind the method in the constructor class using bind.this but again the same error is displayed. also bind the value at the bottom of the component again the same error that cannot read value of props as null
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import SampleData from './data.js';
import _ from 'lodash';
import AppList from './Applist';
import Appointment from './Appointment';
import './App.css';
class App extends Component {
constructor(){
super()
this.state = {
data:[],
aptBodyVisible: false
}
this.deleteMessage = this.deleteMessage.bind(this);
this.toggleAddDisplay = this.toggleAddDisplay.bind(this);
}
componentDidMount(){
this.setState({data: SampleData})
}
deleteMessage(item) {
var allApts = this.state.data;
var newApts = _.without(allApts, item);
this.setState({
data: newApts
});
}
toggleAddDisplay(){
var tempVisibility = !this.state.aptBodyVisible;
this.setState({
aptBodyVisible: tempVisibility
})
}
render() {
var filtered = this.state.data;
filtered = filtered.map((item, index)=>{
return(
<AppList key = { index }
singleItem = { item }
whichItem = { item }
onDelete = {this.deleteMessage}
/>
)
})
return (
<div className="main">
<Appointment
bodyVisible = { this.state.aptBodyVisible }
handleToggle = { this.toggleAddDisplay } />
<ul className="item-list media-list">{filtered} </ul>
</div>
);
}
}
export default App;
child class component
import React, { Component } from 'react';
class AppList extends Component {
handleDelete(){
this.props.onDelete(this.props.whichItem);
}
render(){
return(
<li className="pet-item media">
<div className="media-left">
<button className="pet-delete btn btn-xs btn-danger"
onClick = {this.handleDelete}>
<span className="glyphicon glyphicon-remove"></span></button>
</div>
<div className="pet-head">
<span className="pet-name">{this.props.singleItem.petName}</span>
<span className="apt-date pull-right">{this.props.singleItem.aptDate}</span>
</div>
<div className="owner-name"><span className="label-item">Owner:</span>
{this.props.singleItem.ownerName}</div>
<div className="apt-notes">{this.props.singleItem.aptNotes}</div>
</li>
)
}
}
export default AppList;
From the React Documentation
The constructor for a React component is called before it is mounted. When implementing the constructor for a React.Component subclass, you should call super(props) before any other statement. Otherwise, this.props will be undefined in the constructor, which can lead to bugs.
Like this:
constructor(props){
super(props);
this.state = {
data:[],
aptBodyVisible: false
}
this.deleteMessage = this.deleteMessage.bind(this);
this.toggleAddDisplay = this.toggleAddDisplay.bind(this);
}
yes again we need to bind the method in the child components even to work with the click events
onClick = {this.handleDelete.bind(this)}

Resources