I am trying to add some state data into local storage in a method but I get the error:
'handleFormSubmit' function is not defined no-undef
This is my code:
class App extends React.Component {
state = {
noteTitle: 'sample title',
noteDescription: 'sample desc'
}
handleFormSubmit = () => {
const { noteTitle, noteDescription } = this.state;
localStorage.setItem('noteTitle', noteTitle);
localStorage.setItem('noteDescription', noteDescription);
};
render() {
return (
<input
type="text"
className="form-control"
placeholder="Title"
onChange={(e) => { this.setState({ noteTitle: e.target.value }) }}
/>
<textarea
type="text"
className="form-control my-4"
placeholder="Description"
onChange={(e) => { this.setState({ noteDescription: e.target.value }) }}
/>
<button onClick={handleFormSubmit} className="form-control">Save</button>
I have looked through other post's regarding this issue, it seems so simple but most other issues deal with an extra thing and I don't quite understand why my code shouldn't work.
You need to use both this and () to identify that it is local to the class and that it is a function. Change this...
<button onClick={handleFormSubmit} className="form-control">Save</button>
To this...
onClick={() => this.handleFormSubmit()}
Take a look at a working demo with this fixed.
If you fork the code above, and change it back to the way you had it, I get the same error as you had...
'handleFormSubmit' function is not defined no-undef
Related
Here is a React class I'm working on:
import React from 'react';
export default class ExpenseForm extends React.Component {
state = {
title: ''
};
onTitleChange = (e) => {
const title = e.target.value;
this.setState(() => ({title}));
};
render() {
return (
<div>
<form>
<input
type='text'
placeholder='title'
value={this.state.title}
onChange={(e) => this.setState(() => ({title: e.target.value}))}
required autoFocus/>
<textarea placeholder='Add a note for your expense'/>
<input type='number' placeholder='amount' required/>
<input type='submit' value='Add Expense'/>
</form>
</div>
);
}
}
This throws an error Uncaught TypeError: Cannot read property 'value' of null when executing onChange.
But when I restructure inner js of onChange into a separate function onTitleChange and calling that function: onChange={this.onTitleChange}, it works perfectly. What could be the reason behind this behavior?
Here you assigned onchange event as onChange={(e) => this.setState(() => ({title: e.target.value}))}, here e.target.value will not work, because its inside the setState() scope.
On expanding your function, we will get
function(e){
this.setState(function(){
return {
title: e.target.value
}
})
}
Here there is no e in function inside setSate(), so e.target.value will be error;
Since you dont want to compare with previous value and only need to set the value to title, you can use like
onChange={(e) => this.setState({title: e.target.value})}
There is no need for an extra function inside setState
I would guess, that your parameter e is not known in the inner arrow function.
You could write it like this:
<input
type='text'
placeholder='title'
value={this.state.title}
onChange={e => this.setState({ title: e.target.value })}
required
autoFocus
/>
That is because of React is utilizing event pooling and setState is a function executed in asynchronous context, so you observe them as nulls in the asynchronous setState callback.
You need to either persist event using e.persist() or save the value of event to variable, as in your method.
You don't need to use arrow function inside setstate.
change
onChange={(e) => this.setState(() => ({title: e.target.value}))}
to:
onChange={(e) => this.setState({title: e.target.value})}
sDoing a React course I faced with a strange situation deleting object from state. The course author used non-functional state update but I tried to make it functional and got an error. It would be great if you explain the principal difference in those two approaches.
To begin with state looks so:
{
items: {item11 : {_item1Props_}, item2: {item2Pros}},
order: {_some_suff_in_the_cart}
}
The following code performed for deleting item2:
deleteItem = key => {
const items= {...this.state.items, [key]: null};
this.setState({items});
}
All the components which use state updated with no errors (cart, view and so on).
Trying to make setState functional I got error in render methods of other components with the following code:
deleteItem = key => {
this.setState(prevState =>(
{
items:
{...prevState.items, [key]: null}
}
));
}
Even more the following code failed as well:
deleteItem = key => {
const items= {...this.state.items, [key]: null};
this.setState(prevState => ({items}));
}
So, there must be a principal difference in those methods?
Thank you in advance!
UPD components code:
There are two components using state:
TypeError: _this$props$details is null
Comes from the component which renders item:
class Item extends Component {
render() {
const {name, desc, price, status, image} = this.props.details;
const isAvailable = status === "available";
return (
<li className="menu-item">
<img src={image} alt={name}/>
<h3 className="item-name">
{name}
<span className="price">{formatPrice(price)}</span>
</h3>
<p>{desc}</p>
<button
disabled={!isAvailable}
onClick={()=>{this.props.addToOrder(this.props.index)}}
>
{isAvailable ? "Add To Cart" : "Sold Out"}
</button>
</li>
);
}
}
and
TypeError: this.props.item is null
from the component which is used to edit items:
class EditItemForm extends Component {
handleChange = (e) => {
const updatedItem = {
...this.props.item,
[e.target.name]: e.target.value
};
this.props.updateItem(this.props.index, updatedItem);
}
render() {
return (
<div className="item-edit">
<input
name="name"
type="text"
onChange={this.handleChange}
value={this.props.item.name}
/>
<input
name="price"
type="text"
onChange={this.handleChange}
value={this.props.item.price}
/>
<select
name="status"
value={this.props.item.status}
onChange={this.handleChange}
>
<option value="available">Available</option>
<option value="unavailable">Sold Out</option>
</select>
<textarea
name="desc"
type="text"
onChange={this.handleChange}
value={this.props.item.desc}
/>
<input
name="image"
type="text"
onChange={this.handleChange}
value={this.props.item.image}
/>
</div>
);
}
}
UPD2:
I logged the render() methods of mentioned components I found out that object version removes the property at all, so the null is not passed to the components meanwhile the functional preserves the null value when state is passed to the components.
I am currently building a react application with a .net core back end. My current issue lies in a view that is meant to edit an article (which is made up of only a title and description). On componentDidMount, I am getting the route param id from the route and retrieving the article from the server with it (I've verified that this works correctly). My issue is that my form is not filling out with the fetched data. I'm of the understanding that since the form fields set to this.state... then they should update as the state updates however this is not what I'm seeing. I believe the issue is may lie with the warning I'm receiving in console:
index.js:2177 Warning: A component is changing a controlled input of
type hidden to be uncontrolled. Input elements should not switch from
controlled to uncontrolled (or vice versa). Decide between using a
controlled or uncontrolled input element for the lifetime of the
component.
I've read the documentation the warning points to and am not seeing how my component violates this.
My component is below in full:
import React, { Component } from 'react';
import CKEditor from 'react-ckeditor-component';
export class ArticlesEdit extends Component {
displayName = ArticlesEdit.name
constructor(props) {
super(props);
this.state = {
title: '',
description: ''
};
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount () {
const { id } = this.props.match.params;
fetch(`https://localhost:44360/api/articles/${id}`)
.then((article) => {
this.setState({
title: article.title,
description: article.description
});
});
}
updateDescription(event){
this.setState({description: event.target.value});
}
render() {
return(
<form onSubmit={this.handleSubmit} >
<div className="form-group row" >
<label className=" control-label col-md-12" htmlFor="Title">Title</label>
<div className="col-md-4">
<input className="form-control" type="text" id="title" name="title" defaultValue={this.state.title} required />
</div>
</div >
<CKEditor activeClass="editor" content={this.state.description} events= {{"change": this.onEditorChange.bind(this) }} />
<input type="hidden" id="description" name="description" value={this.state.description} onChange={this.updateDescription}/>
<div className="form-group">
<button type="submit" className="btn btn-default">Save</button>
</div >
</form >
);
}
onEditorChange(evt){
var newContent = evt.editor.getData();
this.setState({
description: newContent
});
}
handleSubmit(event) {
event.preventDefault();
const data = new FormData(event.target);
console.log(this.state.title);
// POST request for Add employee.
fetch('https://localhost:44360/api/articles/', {
method: 'PUT',
body: data
}).then((response) => response.json())
.then((responseJson) => {
this.props.history.push("/articles");
})
}
}
You are not parsing the JSON you get as response to your fetch in componentDidMount. If you add .then((response) => response.json()) it should work as expected.
componentDidMount () {
const { id } = this.props.match.params;
fetch(`https://localhost:44360/api/articles/${id}`)
.then((response) => response.json())
.then((article) => {
this.setState({
title: article.title,
description: article.description
});
});
}
You also need to use the value prop instead of the defaultValue prop on your input so that it will have the value of title in your state.
<input
className="form-control"
type="text" id="title"
name="title"
value={this.state.title}
required
/>
According to React spec:
https://reactjs.org/docs/refs-and-the-dom.html
"There are a few good use cases for refs:
Managing focus, text selection, or media playback.
Triggering imperative animations.
Integrating with third-party DOM libraries.
Avoid using refs for anything that can be done declaratively."
That's why I'm not so sure for now, whether I used ref properly or not in this case:
export let FormInput = createReactClass({
handleInput(e){
e.preventDefault();
const product = this.name.value;
const red = this.red.checked;
this.props.addProduct(product,red);
this.inputForm.reset();
},
render(){
return(
<form className="prod_input" ref={x => this.inputForm = x} onSubmit={this.handleInput}>
<input type="text" ref={x => this.name = x} placeholder="Product name"/>
<input type="checkbox" ref={x => this.red = x} value="true"/>
<input type="submit" hidden/>
</form>
)
}
})
If not, how could it be rearranged in order to replace ref to onChange={}?
You can avoid using refs by making your input fields controlled. Here is an example:
export let FormInput = createReactClass({
constructor(props){
super(props);
this.state = {
name: '',
};
}
handleNameChange(updatedName){
this.setState({ name: updatedName });
}
handleInput(e){
e.preventDefault();
const product = this.state.name;
// ...
this.props.addProduct(product);
this.inputForm.reset();
},
render(){
return (
<form className="prod_input" ref={x => this.inputForm = x} onSubmit={this.handleInput}>
<input type="text" value={this.state.name} placeholder="Product name" onChange={e => this.handleNameChange(e.target.value)} />
// ...
</form>
);
}
})
The idea is to keep the value of your fields in your local state. Then you can set the value of each of your fields to the value currently stored in your state, and attach an onChange handler that updates this value every the user types something. When you submit your form, all your values are available directly in your component's state.
I am working on a project which is basically notepad. I am having problems though updating the <textarea>'s value when an ajax call is made. I tried setting the textarea's value property but then no changes to its value can be made. How can I make it so on a state change the textarea's value changes and can be edited.
The code I have is as follows.
In the parent class
<Editor name={this.state.fileData} />
In the Editor class
var Editor = React.createClass({
render: function() {
return (
<form id="noter-save-form" method="POST">
<textarea id="noter-text-area" name="textarea" value={this.props.name}></textarea>
<input type="submit" value="Save" />
</form>
);
}
});
I can't use defaultValue because the value of the textarea is not known on page load and when I try and put the data between the textareas nothing happens. I would like it to take the state value whenever the state changes but have it editable in between.
Thanks
Edit
I managed to get it working using jQuery but would like to do it in React instead, I called this before render:
$('#noter-text-area').val(this.props.name);
I think you want something along the line of:
Parent:
<Editor name={this.state.fileData} />
Editor:
var Editor = React.createClass({
displayName: 'Editor',
propTypes: {
name: React.PropTypes.string.isRequired
},
getInitialState: function() {
return {
value: this.props.name
};
},
handleChange: function(event) {
this.setState({value: event.target.value});
},
render: function() {
return (
<form id="noter-save-form" method="POST">
<textarea id="noter-text-area" name="textarea" value={this.state.value} onChange={this.handleChange} />
<input type="submit" value="Save" />
</form>
);
}
});
This is basically a direct copy of the example provided on https://facebook.github.io/react/docs/forms.html
Update for React 16.8:
import React, { useState } from 'react';
const Editor = (props) => {
const [value, setValue] = useState(props.name);
const handleChange = (event) => {
setValue(event.target.value);
};
return (
<form id="noter-save-form" method="POST">
<textarea id="noter-text-area" name="textarea" value={value} onChange={handleChange} />
<input type="submit" value="Save" />
</form>
);
}
Editor.propTypes = {
name: PropTypes.string.isRequired
};
As a newbie in React world, I came across a similar issues where I could not edit the textarea and struggled with binding. It's worth knowing about controlled and uncontrolled elements when it comes to react.
The value of the following uncontrolled textarea cannot be changed because of value
<textarea type="text" value="some value"
onChange={(event) => this.handleOnChange(event)}></textarea>
The value of the following uncontrolled textarea can be changed because of use of defaultValue or no value attribute
<textarea type="text" defaultValue="sample"
onChange={(event) => this.handleOnChange(event)}></textarea>
<textarea type="text"
onChange={(event) => this.handleOnChange(event)}></textarea>
The value of the following controlled textarea can be changed because of how
value is mapped to a state as well as the onChange event listener
<textarea value={this.state.textareaValue}
onChange={(event) => this.handleOnChange(event)}></textarea>
Here is my solution using different syntax. I prefer the auto-bind than manual binding however, if I were to not use {(event) => this.onXXXX(event)} then that would cause the content of textarea to be not editable OR the event.preventDefault() does not work as expected. Still a lot to learn I suppose.
class Editor extends React.Component {
constructor(props) {
super(props)
this.state = {
textareaValue: ''
}
}
handleOnChange(event) {
this.setState({
textareaValue: event.target.value
})
}
handleOnSubmit(event) {
event.preventDefault();
this.setState({
textareaValue: this.state.textareaValue + ' [Saved on ' + (new Date()).toLocaleString() + ']'
})
}
render() {
return <div>
<form onSubmit={(event) => this.handleOnSubmit(event)}>
<textarea rows={10} cols={30} value={this.state.textareaValue}
onChange={(event) => this.handleOnChange(event)}></textarea>
<br/>
<input type="submit" value="Save"/>
</form>
</div>
}
}
ReactDOM.render(<Editor />, document.getElementById("content"));
The versions of libraries are
"babel-cli": "6.24.1",
"babel-preset-react": "6.24.1"
"React & ReactDOM v15.5.4"