React onClick function within render() is undefined - javascript

With the react component below I am getting the error:
Uncaught (in promise) TypeError: Cannot read property 'oncontactClick' of undefined
class Contacts extends React.Component{
constructor(props, contact){
super(props);
this.state ={ contacts: [] };
this.oncontactClick = this.oncontactClick.bind(this);
}
oncontactClick(){
console.log("contact clicked");
}
render(){
var contacts = this.state.contacts;
contacts = contacts.map(function(contact, index){
return(
<div key={index} className="contactcard" onClick={this.oncontactClick}>
<div className="name">{contact.name}</div>
</div>
)
});
return(
<div id="addcontainer">
<form id="addinput">
<input type="text" ref="name" placeholder="name" required/>
</form>
<ul>{contacts}</ul>
</div>
);
}
};
However, if I place the onClick in the form "addinput", it works fine. Why is that the case and how can I get it to work within the contactcard? I would like to avoid using jQuery.

Array.map takes two arguments :
First one is the iteratee.
Second one is what defines this in the iteratee.
This should works :
contacts = contacts.map(function(contact, index){
return(
<div key={index} className="contactcard" onClick={this.oncontactClick}>
<div className="name">{contact.name}</div>
</div>
)
}, this);

Related

Uncaught TypeError - Getting errors after already defining state variable in React component constructor

I am getting the error: Uncaught TypeError: Cannot read properties of undefined (reading 'state') even though state is defined in the constructor of my React component. I get the error at the line where I set the value of the <input> to {this.state.deckName}
export class DeckForm extends React.Component {
constructor(props) {
super(props);
this.state = {
deckName: '',
deckList: ''
};
// Bind our event handler methods to this class
this.handleDeckNameChange = this.handleDeckNameChange.bind(this);
this.handleDeckListChange = this.handleDeckListChange.bind(this);
this.handleSubmission = this.handleSubmission.bind(this);
}
// Event handler method to update the state of the deckName each time a user types into the input form element
handleDeckNameChange(event) {
let typed = event.target.value;
this.setState({ deckName: typed });
}
// Event handler method to update the state of the deckList each time a user types into the textarea from element]
handleDeckListChange(event) {
let typed = event.target.value;
this.setState({ deckList: typed });
}
// Event handler method to handle validation of deckName and deckList
handleSubmission(event) {
console.log(`${this.state.deckName}`);
console.log(`${this.state.deckList}`)
}
render() {
return (
<form className='was-validated'>
<this.DeckName />
<this.DeckList />
<button type='submit' className='btn-lg btn-warning mt-3'>Create</button>
</form>
);
}
DeckName() {
return (
<div className='form-group mb-3'>
<input
value={this.state.deckName} /* ERROR HERE */
onChange={this.handleDeckNameChange}
type='text'
placeholder='Deck name'
className='form-control'
required
/>
</div>
);
}
DeckList() {
let format = 'EXACT CARD NAME 1\nPot of Greed 3\nChange of Heart 3\nGraceful Charity 3'
return (
<div className='form-group'>
<textarea
value={this.state.deckList}
onChange={this.handleDeckListChange}
className='form-control'
rows='15'
required
>
{format}
</textarea>
</div>
);
}
}
Use below code it's working for me
https://codesandbox.io/s/weathered-water-jm1ydv?file=/src/App.js
DeckName() {
return (
<div className="form-group mb-3">
<input
value={this?.state.deckName} /* ERROR HERE */
onChange={this?.handleDeckNameChange}
type="text"
placeholder="Deck name"
className="form-control"
required
/>
</div>
);
}
DeckList() {
let format =
"EXACT CARD NAME 1\nPot of Greed 3\nChange of Heart 3\nGraceful Charity 3";
return (
<div className="form-group">
<textarea
value={this?.state.deckList}
onChange={this?.handleDeckListChange}
className="form-control"
rows="15"
required
>
{format}
</textarea>
</div>
);
}
You can use es6 function to return components which exist outside of parent rather than using method,change only this part of code:
1.instead of DeckName(){...}use DeckName =()=>{....}
2.instead of DeckList(){...}use DeckList =()=>{....}
Full modified code:
import React, { Component } from "react";
export class DeckForm extends Component {
constructor(props) {
super(props);
this.state = { deckName: "", deckList: "" };
// Bind our event handler methods to this class
this.handleDeckNameChange = this.handleDeckNameChange.bind(this);
this.handleDeckListChange = this.handleDeckListChange.bind(this);
this.handleSubmission = this.handleSubmission.bind(this);
}
// Event handler method to update the state of the deckName each time a user types into the input form element
handleDeckNameChange(event) {
let typed = event.target.value;
this.setState({ deckName: typed });
}
// Event handler method to update the state of the deckList each time a user types into the textarea from element]
handleDeckListChange(event) {
let typed = event.target.value;
console.log(typed);
this.setState({ deckList: typed });
}
// Event handler method to handle validation of deckName and deckList
handleSubmission(event) {
console.log(`${this.state.deckName}`);
console.log(`${this.state.deckList}`);
}
render() {
return (
<form className="was-validated">
<this.DeckName />
<this.DeckList />
<button type="submit" className="btn-lg btn-warning mt-3">
Create
</button>
</form>
);
}
DeckName = () => {
return (
<div className="form-group mb-3">
<input
value={this.state.deckName}
onChange={this.handleDeckNameChange}
type="text"
placeholder="Deck name"
className="form-control"
required
/>
</div>
);
};
DeckList = () => {
let format =
"EXACT CARD NAME 1\nPot of Greed 3\nChange of Heart 3\nGraceful Charity 3";
return (
<div className="form-group">
<textarea
value={this.state.deckList}
onChange={this.handleDeckListChange}
className="form-control"
rows="15"
required
>
{format}
</textarea>
</div>
);
};
}
Live Demo:
https://codesandbox.io/s/brave-hill-l0eknx?file=/src/DeckForm.js:0-2091
Another way to solve the issue is defining DeckName() method using arrow function. Here's a code snippet I tried with react 17.0.2 which worked perfectly fine for me.
It's always recommended to use arrow function to define methods in class based components, since arrow function inherit "this" from the block its called from, so you also don't have to do .bind(this) whenever you call methods.
JSX
import React, { Component } from 'react'
class Test extends Component {
constructor(props) {
super(props);
this.state = {
deckName: '',
deckList: ''
};
}
DeckName = () => {
return (
<div className='form-group mb-3'>
<input
value={this.state.deckName} /* ERROR HERE */
onChange={this.handleDeckNameChange}
type='text'
placeholder='Deck name'
className='form-control'
required
/>
</div>
);
}
render() {
return (
<form className='was-validated'>
<this.DeckName />
<button type='submit' className='btn-lg btn-warning mt-3'>Create</button>
</form>
);
}
}
export default Test

How to pass onChange input value to parent component in React js

I am trying to pass onChange input value from child component to parent component in react js. I pass with props. But in the component, it writes value as location: <input />. As I understand it return value as object but when I try to convert with Json.stringfy it returns an error. So how can pass and set this value in parent component?
class Search extends Component {
// Define Constructor
constructor(props) {
super(props);
render() {
return (
<div>
<Script url="https://maps.googleapis.com/maps/api/jskey=Key&libraries=places"
onLoad={this.handleScriptLoad}
/>
<input onChange={(e)=>this.props.handleChangeSearch(e)} defaultValue={this.props.location} id="autocomplete" placeholder="search city..."
style={{
margin: '0 auto',
maxWidth: 800,
}}
/>
</div>
);
}
}
export default Search;
Main Component
class MainPage extends Component {
constructor(props) {
super(props);
this.state = {
location: ""
}
}
componentDidMount(){
this.callLoc();
}
handleChangeSearch=(event)=> {
// console.log(JSON.stringify(event.target)+" event");
this.setState({location: event.target});
}
render() {
return (
<div id="main">
<Script url="https://maps.googleapis.com/maps/api/js?key=your_api_key&libraries=places"
onLoad={this.handleScriptLoad} />
<h3 id="mainText">Choose Your Next Destination</h3>
<div id='card'>
<div class="input-group">
<Search handleChangeSearch={this.handleChangeSearch} location={this.state.location}/>
<Button onClick={this.searchLoc}>Search</Button>
</div>
<br></br>
<Button onClick={()=>this.callLoc()} block>Near by Guides</Button>
</div>
</div>
);
}
event.target will just point to the element that generated this event, you need to use event.target.value to get the value of the input.
React uses synthetic events (or read here), so passing the event object can lead to a stale object.
// Pass the value and not the event object
<input onChange={ (e) => this.props.handleChangeSearch(e.target.value) } />
// Fix the handler
handleChangeSearch = (value) => { ... }

Grabbing the value of a form input method for a possible arithmetic operation in ReactJS

This is my first question here after years, so pardon me if I break any forum/platform rule.
I am trying to build a CGPA CALCULATOR so I am having an issue updating a variable on user input change.
I am a beginner so my code and description may be watery. The problem is with my handleChange method I guess, because every time I make an input (I am testing with the courseInput for now), the app crashes with the error:
TypeError: Cannot read property 'setState' of undefined
Someone should please explain to me in details.
I have actually tried a lot Googling but nothing seems wrong with my code.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
// this.courseInput = React.createRef();
this.state = {
courseInput: [],
courseCode: '',
courseUnit: [0],
courseGrade: [],
totalPoint: 0,
totalUnit: 0,
newCourseInput: <form>
<input onChange={this.handleChange} type="text" placeholder='COURSE CODE' value={this.courseCode} />
{/* <input type="number" placeholder='COURSE UNIT' ref={this.courseUnit} value={this.courseUnit} />
<input type="number" placeholder='COURSE GRADE' ref={this.courseGrade} value={this.courseGrade} /> */}
</form>
};
this.createAnother = this.createAnother.bind(this);
this.handleChange = this.handleChange.bind(this);
}
// THIS createAnother TAKES THE CURRENT STATE OF courseInput AND CONCATENATES IT WITH THE newCourseInput TO MAKE LIST
createAnother() {
var courseInput = this.state.courseInput.concat(this.state.newCourseInput)
this.setState({ courseInput })
}
handleChange(event) {
var updatedCourseCode = event.target.value;
this.setState({ courseInput: updatedCourseCode }, () => console.log(this.state))
}
render() {
// console.log(this);
// var courseInput = this.state.courseInput;
return(
<div>
<header className="App-header">
<p>
THIS IS A CGPA CALCULATOR
</p>
</header>
{/* MAP FUNCTION LOOPS THROUGH THE ARRAY courseInput AND PRINTS OUT THE CODE UNIT AND GRADE IN IN ORDERED LIST */}
<ol>
{this.state.courseInput.map((courseInput, index) =>
<li key={index}>{courseInput}</li>
)}
</ol>
{/* THIS TRIGGERS AN EVENT HANDLER createAnother LOCATED UP THERE */}
<button onClick={this.createAnother} >ADD ANOTHER COURSE</button>
</div>
);
}
}
export default App;
You should not store jsx elements in your state, but only the necessary data to render these elements later when needed. you also have a mistakes(you tried to assign string to an courseInput whice is array).
import React, { Component } from "react";
// import './App.css';
class App extends Component {
constructor(props) {
super(props);
// this.courseInput = React.createRef();
this.state = {
courseInput: [],
courseCode: "",
courseUnit: [0],
courseGrade: [],
totalPoint: 0,
totalUnit: 0,
};
}
// THIS createAnother TAKES THE CURRENT STATE OF courseInput AND CONCATENATES IT WITH THE newCourseInput TO MAKE LIST
createAnother = () => {
var courseInput = this.state.courseInput.concat({
id: this.state.courseInput.length,
value: "",
});
this.setState({ courseInput });
};
handleCourseChange = (value, id) => {
const newCourseInputs = [...this.state.courseInput];
console.log(newCourseInputs);
console.log(value, id);
let courseToChange = newCourseInputs.find((c) => c.id == id);
courseToChange.value = value;
this.setState({ courseInput: newCourseInputs });
};
render() {
// console.log(this);
// var courseInput = this.state.courseInput;
console.log(this.state.courseInput);
return (
<div>
<header className="App-header">
<p>THIS IS A CGPA CALCULATOR</p>
</header>
{/* MAP FUNCTION LOOPS THROUGH THE ARRAY courseInput AND PRINTS OUT THE CODE UNIT AND GRADE IN IN ORDERED LIST */}
<ol>
{this.state.courseInput.map((courseInput, index) => (
<li key={index}>
<input
onChange={(e) =>
this.handleCourseChange(e.target.value, courseInput.id)
}
type="text"
placeholder="COURSE CODE"
value={courseInput.value}
/>
</li>
))}
</ol>
{/* THIS TRIGGERS AN EVENT HANDLER createAnother LOCATED UP THERE */}
<button onClick={this.createAnother}>ADD ANOTHER COURSE</button>
</div>
);
}
}
export default App;
this code will probably work as you intended.
in handleChange use arrow function instead of regular function :
class A {
handleChange(event){
this // the keyword "this" refer to the function handleChange
}
}
class A {
handleChange =(event)=>{
this // the keyword "this" refer to the class A
}
}
The Difference Between Regular Functions and Arrow Functions : read-me
You're not binding this to handleChange correctly in input tag at
<input onChange={this.handleChange} type="text" placeholder='COURSE CODE' value={this.courseCode} />
You should update onChange function to onChange={this.handleChange.bind(this)}
this is actually a binding that is made when a function is invoked, and what it references is determined entirely by the call-site where the function is called, not where it is declared. More at https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/this%20%26%20object%20prototypes/ch2.md

Uncaught TypeError: Cannot read property 'props' of null in React Class [duplicate]

This question already has answers here:
Unable to access React instance (this) inside event handler [duplicate]
(19 answers)
Closed 6 years ago.
I have refactored a component and i am not using React.createClass in class methods anymore but i have this error now
{this.props.removeComment.bind(null, this.props.params.svgId, i)}
TypeError: Cannot read property 'props' of undefined
The code was perfectly working
Before the refactor
import React from 'react';
const Comments = React.createClass({
renderComment(comment, i) {
return (
<div className="comment" key={i}>
<p>
<strong>{comment.user}</strong>
{comment.text}
<button className="remove-comment" onClick={this.props.removeComment.bind(null, this.props.params.svgId, i)}>×</button>
</p>
</div>
)
},
handleSubmit(e) {
e.preventDefault();
const { svgId } = this.props.params;
const author = this.refs.author.value;
const comment = this.refs.comment.value;
this.props.addComment(svgId, author, comment);
this.refs.commentForm.reset();
},
render() {
return (
<div className="comments">
{this.props.svgComments.map(this.renderComment)}
<form ref="commentForm" className="comment-form" onSubmit={this.handleSubmit}>
<input type="text" ref="author" placeholder="author"/>
<input type="text" ref="comment" placeholder="comment"/>
<input type="submit" hidden />
</form>
</div>
)
}
});
export default Comments;
Now after the refactor
import React from 'react';
export default class Comments extends React.Component {
renderComment(comment, i) {
return (
<div className="comment" key={i}>
<p>
<strong>{comment.user}</strong>
{comment.text}
<button className="remove-comment" onClick={this.props.removeComment.bind(null, this.props.params.svgId, i)}>×</button>
</p>
</div>
)
};
handleSubmit(e) {
e.preventDefault();
const { svgId } = this.props.params;
const author = this.refs.author.value;
const comment = this.refs.comment.value;
this.props.addComment(svgId, author, comment);
this.refs.commentForm.reset();
};
render() {
return (
<div className="comments">
{this.props.svgComments.map(this.renderComment)}
<form ref="commentForm" className="comment-form" onSubmit={this.handleSubmit}>
<input type="text" ref="author" placeholder="author"/>
<input type="text" ref="comment" placeholder="comment"/>
<input type="submit" hidden />
</form>
</div>
)
}
};
So how can i manually bind this in class constructor ?
you need to bind the methods to the component instance in the constructor like so
export default class Comments extends React.Component {
constructor() {
super();
this.renderComment = this.renderComment.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
if you are using babel with stage-2 you can also refactor your methods and just do the following:
renderComment = (comment, i) => {
// code goes here
}
handleSubmit = (e) => {
// code goes here
}
i prefer the second way as its much cleaner but have to have the right plugin for babel for it to work properly.
What this is doing is making sure when these functions are called, they are called with this being bound to the component.
you should have a constructor, call the super() and there bind the method
React.createClass automatically bind this to the component, in ES6 class you have to make it manually, and you can not use this until super() has been called

Cannot read property 'length' of undefined[reactjs]

I am trying to count the remaining character using reactjs. I have defined the function which talks with the state and is passed down to the child component. In the child component i am getting an error of Uncaught TypeError: Cannot read property 'length' of undefined .
app.js
export default class App extends Component {
constructor(props){
super(props);
this.state = {max_char:32};
this.handleChange = this.handleChange.bind(this);
}
handleChange(charLength){
console.log('charLength');
this.setState({
max_char:32 - charLength.length
});
console.log(this.state.max_char);
}
render() {
return (
<div>
<Layout fixedHeader>
<Content>
<div className="page-content">
<DeviceEventList />
<DeviceDialog onChange={this.handleChange} />
</div>
<div className="empty-space">
</div>
</Content>
</Layout>
</div>
);
}
}
device-dialog.js
class DeviceDialog extends Component {
constructor(props) {
super(props);
console.log('this.props',this.props.onChange);
}
handleInputChange(event){
console.log(event.target.value);
let name = event.target.value;
this.props.onChange(name);
}
renderOwnADevice(){
console.log('open',this.props.active_device_event.open);
return(
<div className="device-action">
<Dialog open={this.props.active_device_event.open} onCancel={this.props.CancelDeviceEvent}>
<DialogContent>
<Textfield
onChange={()=> {this.handleInputChange(event)}}
pattern="-?[0-9]*(\.[0-9]+)?"
error="Input is not a number!"
label="Device Serial Number"
floatingLabel
/>
<span style={{ float:'right'}}>character</span>
</DialogContent>
</Dialog>
</div>
)
}
render() {
if ( !this.props.active_device_event){
return <h5 style={{ textAlign:'center' }}>Click icon based on your work</h5>;
}
let icon_name = this.props.active_device_event.icon_name;
if( icon_name == 'devices_other'){
return (<div>Device Other</div>);
}
if( icon_name == 'add_circle_outline'){
return (this.renderOwnADevice());
}
}
}
I would guess onChange={()=> {this.handleInputChange(event)}} should be onChange={(event) => {this.handleInputChange(event)}}. Now you're passing an event variable that's not defined.
As an aside: probably also better to bind the handler in the constructor like you did in app.js instead of having an anonymous function wrapper in your render.
event is never defined in this line: onChange={()=> {this.handleInputChange(event)}}. Therefore your handleChange function is receiving an undefined value, not a string.
<Textfield
onChange={this.handleInputChange}
pattern="-?[0-9]*(\.[0-9]+)?"
error="Input is not a number!"
label="Device Serial Number"
floatingLabel
/>
Your this.handleInputChange will now properly be passed the event argument.

Categories