I want to append the pervious value of state with the current value of the input box
but when i use setState function its somehow not allowing to pass event as argument.
Problem:
setState(prev,props,e)
The 3rd argument (e) is not getting detected by setState function.
e.target.value is coming undefined
This is my code :
import React , {Component} from 'react'
class Todo extends Component{
constructor(){
super()
this.state={toDo:[],currentInput:"name"}
// bind the events
this.addItem=this.addItem.bind(this)
this.changeList=this.changeList.bind(this)
}
addItem(e){
// prevent default
e.preventDefault();
// this part is left
}
changeList(e){
this.setState(function (prev,props,e){
console.log(prev.currentInput)
return {currentInput:prev.currentInput+e.target.value} // this part is causing problem
})
}
render(){
return(
<div className="App">
<h1>Add your tasks</h1>
<form onSubmit={this.addItem}>
<input type="text" value={this.state.currentInput} onChange={this.changeList} ></input>
<input type="submit"></input>
</form>
<div className="List">
<h3>Remaining items:-</h3>
<ul>
</ul>
</div>
</div>
)
}
}
export default Todo
That's not how setState works, you can't pass additional arguments, if you pass a function, React will invoke it passing in the current state and the props as arguments
Worth mentioning that if you use a functional update you have to capture the input value before you call setState or call event.persist() as React will clear the event after the handler has been invoked setting event.target to null
Event polling
Example
changeList(e){
const { value } = e.target
// or call e.persist() and use e.target.value inside the function
// now it's safe to use a functional update
this.setState(prevState => ({
currentInput: prevState.currentInput + value
}))
}
You also don't want to append to the previous input state as e.target.value contains the full input value so you can just pass an object into setState
changeList(e){
this.setState({ currentInput: e.target.value })
}
you don't actually need to do prev.currentInput+e.target.value as e.target.value will be the current input value ... if you type alpha in input it will actually evaluate to namenamealpha whereas my guess is you are expecting namealpha
but if you still wanna make it work here is how you should do it
import React , {Component} from 'react'
class Todo extends Component{
constructor(){
super()
this.state={toDo:[],currentInput:"name"}
}
addItem = (e) => {
e.preventDefault();
}
changeList = (e) => {
const newValue = e.target.value;
this.setState((state) => {
console.log({state, newValue})
return({
currentInput: state.currentInput + newValue
})
});
}
render(){
return(
<div className="App">
<h1>Add your tasks</h1>
<form onSubmit={this.addItem}>
<input type="text" value={this.state.currentInput} onChange={this.changeList} ></input>
<input type="submit"></input>
</form>
<div className="List">
<h3>Remaining items:-</h3>
<ul>
</ul>
</div>
</div>
)
}
}
export default Todo
Alternatively and ideally you can also use hooks like this
import React , { useState }from 'react'
const Todo = () => {
const [currentInput, setCurrentInput] = useState('name');
return(
<div className="App">
<h1>Add your tasks</h1>
<form onSubmit={() => console.log('submit')}>
<input type="text" value={currentInput} onChange={(e) => setCurrentInput(e.target.value)} ></input>
<input type="submit"></input>
</form>
<div className="List">
<h3>Remaining items:-</h3>
<ul>
</ul>
</div>
</div>
)
}
export default Todo
Related
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
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) => { ... }
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
I have a React file which displays a list of city data as a component. there is an input textbox above it which needs to accept user input. i am using state to display an initial string in the textbox, but i cannot get onChange to successfully use a function to setState. troubleshooting it with console.log i can see that when i attempt to change the state the function i am pointing to with onChange does work and changes one letter, but then the state snaps back to its default value. the problem seems to be with setState not saving the change and reverting back to the initial state after any changes are made. the text box content appears to not change at all, thought console.log shows a one letter change but then reverts back to the original state.
how do i update state? i want the user to be able to punch a number in and then compare it with the list.
import React, {Component} from 'react';
import Table from './Table';
import cities from './Cities';
class App extends Component {
state = {
userInput: "Your City Population"
}
popChanger = (event) => {
this.setState( {userInput: event.target.value} );
//console.log(event.target.value);
}
yourCity = (
<div>
<input
type='text'
onChange={this.popChanger}
value={this.state.userInput}
/>
</div>
)
render() {
return (
<div className = "App">
{this.yourCity}
<Table characterData = {cities} />
</div>
);
}
}
export default App;
setState() is saving your changes, just not in the right place,
popChanger() is an arrow function and updates the state of the App component,
yourCity has it's own this so it doesn't know about the App state.
you can either cahnge yourCity to an arrow function that returns the html you want like
class TodoApp extends React.Component {
state = {
a: ''
};
YourCity = () => (
<div>
<input type="text" onChange={this.handleChange} value={this.state.a} />
</div>
}
handleChange = e => this.setState({a : e.target.value})
render() {
return (
<div>
<this.YourCity />
</div>
)
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
Or, create yourCity component outside and pass the handleChange as a prop :
const YourCity = props => (
<div>
<input type="text" onChange={props.handleChange} value={props.value} />
</div>
)
class TodoApp extends React.Component {
state = {
a: ''
};
handleChange = e => this.setState({a : e.target.value})
render() {
return (
<div>
<YourCity handleChange={this.handleChange} value={this.state.a}/>
</div>
)
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
The state is updating but you can't see that because this.yourCity doesn't re-render
popChanger = (event) => {
this.setState( {userInput: event.target.value} );
console.log(event.target.value);
}
yourCity(){
return <div>
<input
type='text'
onChange={this.popChanger}
value={this.state.userInput}
/>
</div>
}
render() {
return (
<div className = "App">
{this.yourCity()}
</div>
);
}
}
I recently got started with React and want to build a little application to fetch weather data. My API has a function to return autocomplete suggestions. So when my autosuggestion array is not empty I render a list and upon clicking one of the <li>'s I want the value inside of the input box. I manage to set the state of my SearchBar but can't change it's value.
Edit: I try to get my value from changeState() into my <input type="text" placeholder="City, Zip Code, Coordinates" onChange={evt => this.updateInputValue(evt)} />. I can search for terms otherwise.
import React from 'react';
import './SearchBar.css';
import Suggestion from './Suggestion';
class SearchBar extends React.Component{
constructor(props) {
super(props);
this.state = {inputValue: ''};
this.search = this.search.bind(this);
this.updateInputValue = this.updateInputValue.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
this.changeState = this.changeState.bind(this);
}
changeState(value) {
console.log(value);
// Logs value of text between <li></li>
this.setState({inputValue: value});
}
search() {
this.props.onSearch(this.state.inputValue);
}
updateInputValue(evt) {
this.setState({
inputValue: evt.target.value
});
this.props.onChange(this.state.inputValue);
}
handleKeyPress(e) {
if(e.key === 'Enter') {
this.search();
}
}
render() {
return (
<div>
<div className="SearchGroup" onKeyPress={this.handleKeyPress} >
<input type="text" placeholder="City, Zip Code, Coordinates" onChange={evt => this.updateInputValue(evt)} />
<a onClick={this.search}>Go</a>
</div>
<Suggestion autocomplete={this.props.autocomplete} onSelect={this.changeState} />
</div>
);
}
}
export default SearchBar;
For the sake of completeness my Suggestion.js:
import React from 'react';
import './Suggestion.css';
class Suggestion extends React.Component{
constructor(props) {
super(props);
this.updateInputField = this.updateInputField.bind(this);
}
updateInputField(evt) {
this.props.onSelect(evt.currentTarget.innerText);
}
render(){
if(this.props.autocomplete && this.props.autocomplete.length > 0) {
return (
<div className="Suggestion">
<ul>
{
this.props.autocomplete.map((location) => {
return (
<li key={location.id} onClick={this.updateInputField}>{location.name}</li>
)
})
}
</ul>
</div>
);
} else {
return <div className="None"></div>
}
}
}
export default Suggestion;
I would also prefer to submit location.url in Suggestion, but I could not find a property that matches inside of evt.
As mentioned in my comment. You are setting state and immediately passing state to onChange function in updateInputValue event handler function which is not correct. Because you won't get the state value updated immediately, the state value updates only when it renders so, pass evt.target.value directly like below
updateInputValue(evt) {
this.setState({ inputValue: evt.target.value });
this.props.onChange(evt.target.value);
}
In order to see chnaged value on your input field, you have to pass value prop to input tag like below
<input type="text" placeholder="City, Zip Code, Coordinates" onChange={evt => this.updateInputValue(evt)} value={this.state.inputValue}/>
I would guess that you are trying to use value from state that isnt there yet, because setState is asynchronous
so either use callback on setState
updateInputValue(evt) {
this.setState({
inputValue: evt.target.value
}, ()=> this.props.onChange(this.state.inputValue));
}
or, use the value from event directly
updateInputValue(evt) {
const value = evt.target.value
this.setState({
inputValue: value
});
this.props.onChange(value)
}
plus you havent assigned value back to your input:
<input type="text" placeholder="City, Zip Code, Coordinates" onChange={evt => this.updateInputValue(evt)} value={this.state.inputValue}/>
The React setState doesn't update the state immediately. It puts it in the queue and updates the state in batches. if you want to access the updated state write the code in the setState callBack
this.setState({ inputValue: evt.target.value},()=> this.props.onChange(this.state.inputValue));
something like this