React: how do I use onSubmit to change state? - reactjs

I'm quite new to React, and have only completed a few projects with it. I'm currently trying to create a form that, using onSubmit, changes the state of "isSubmitted" from false to true. When "isSubmitted" is true, I'd like to render another component to show the results of the selection.
What's currently happening is that onChange is working and I can see the value of "selectedI" set as state in the console.log when I change it. However, when I click submit, this state of "isSubmitted" doesn't change.
My code is below. Any help is greatly appreciated!
import React, { Component } from "react";
import Results from "../components/Results";
export class Create extends Component {
constructor(props) {
super(props);
this.state = {
selectedI: { value: "" },
// selectedC: { value: "" },
// selectedG: { value: "" },
// selectedA: { value: "" },
isSubmitted: false,
};
}
handleChange = (event) => {
this.setState({
selectedI: { value: event.target.value },
});
};
handleSubmit = (event) => {
event.preventdefault();
this.setState({
isSubmitted: true,
});
};
render() {
console.log(this.state);
return (
<>
<form onSubmit={this.handleSubmit} onChange={this.handleChange}>
<select value={this.state.value}>
{this.props.ingredient.map((ingredient) => {
return (
<option value={ingredient.strIngredient1}>
{ingredient.strIngredient1}
</option>
);
})}
</select>
<input type="submit" value="Submit" />
</form>
{this.state.isSubmitted && <Results />}
</>
);
}
}
export default Create;

Inside your handleSubmit method correct the case on preventdefault. It should be preventDefault. Note the capital D. Once corrected it should stop your page from reloading and resetting your state along with it. See the code below.
handleSubmit = (event) => {
event.preventDefault();
this.setState({
isSubmitted: true,
});
};

Related

Unable to change the value in the object

My objective is to change the value of the object to true or false while onchanging the checkbox.
Object contains:
{
id: '12497wewrf5144',
name: 'ABC',
isVisible: 'false'
}
Here is the code:
import React, { Component } from 'react'
class Demo extends Component {
constructor(props) {
super(props)
this.state = {
demo: {}
}
}
componentDidMount() {
axios
.get('/api/random')
.then(res => {
this.setState({ demo: res.data?.[0] })
})
.catch(error => {
console.log(error)
})
}
render() {
return (
<div>
<h1>{this.state.demo.name}</h1>
<input type="checkbox" value={this.state.demo.value} />
</div>
)
}
}
export default Demo
I don't know what to write in onchange method for checkbox to only change the value within the object.
Can anyone help me in this query?
<input
type="checkbox"
value={this.state.demo.value}
onChange={(event) => {
this.setState((prevState) => ({
...prevState,
demo: { ...prevState.demo, isVisible: event.target.checked }
}));
}}
/>
Given your state ends up looking like
this.state = {
demo: {
id: "12497wewrf5144",
name: "ABC",
isVisible: "false",
value: false
}
};
You can create a change handler as such
changeHandler = e => {
e.preventDefault();
const { checked } = e.target;
this.setState(prevState => ({
...prevState, // <-- spread existing state
demo: {
...prevState.demo, // <-- spread existing demo
value: checked, // <-- save the input's checked value
}
}))
}
Attach the changeHandler to the onChange event callback
<input
type="checkbox"
onChange={this.changeHandler}
value={this.state.demo.value}
/>
Ciao, you could use onClick event like this:
...
handleClick = (e, data) => {
const demo = { ...this.state.demo };
demo.isVisible = !demo.isVisible;
this.setState({ demo });
}
...
<input type="checkbox" value={this.state.demo.value} onClick={((e) => this.handleClick(e, data))}/>
...

Can't type in React input text field in Todo app

I am making a todo app in React.js but stuck in this part. I can not type in input field. Please help me.
import React, { Component } from 'react';
export default class AddItem extends Component {
state =
{
title: "",
done: false
}
changeTitle = (e) =>{
e.preventDefault();
this.setState = ({
title: e.target.value
});
}
addNewItem = (item) => {
item.preventDefault();
let newTask = {
title: this.state.title,
done: false
};
this.props.additem(newTask);
this.setState = ({
title: ""
});
}
render() {
return (
<div>
<form>
<input
type="text"
placeholder="add task name."
value={this.state.title}
onChange= {this.changeTitle}
/>
<button type = "button" onClick= {this.addNewItem} >submit</button>
</form>
</div>
)
}
}
this.setState is a function that is called with an object containing changes in state. The code you are using here is an assignment not a function call:
this.setState = ({
title: e.target.value // Wrong
});
Instead, call the setState function with the changes/updates in state. the changes are shallow merged and only title is updated here.
this.setState({title:e.target.value});
You will have a similar problem inside addNewItem.

How to update state inside componentDidMount?

I'm using fetch API and I want update the const called state inside the componentDidMount() (with onChange) which are being using in a template string. How do I update this value with onChange?
import React, {Component} from 'react'
class Data extends Component {
constructor() {
super();
this.state = {
items: {},
value: '',
isLoaded: false
}
}
handleChange(e) {
this.setState({value: e.target.value});
}
componentDidMount() {
const state = this.state.value
fetch(`http://api.timezonedb.com/v2.1/get-time-zone?key=J9X3EOT2EM8U&format=json&by=zone&zone=${state}`)
.then(res => res.json())
.then(json => {
this.setState({
isLoaded: true,
items: json,
})
});
}
render(){
const {isLoaded} = this.state;
if(!isLoaded) {
return <div>Loading...</div>
}
return(
<div>
<select onChange={this.handleChange}>
<option value="America/Chicago">Chicago</option>
<option value="America/Sao_Paulo">São Paulo</option>
</select>
</div>
)
}
}
So, how can I update the value of the const state with onChange?
componentDidMount() is called when the React component has mounted, and it happens only once.
If I understand correctly, you want to call fetch on each change of the value stored under value state property, so the componentDidMount method is not a perfect place to put that kind of logic. You can create a separate method called fetchData and pass the value to it as an argument. Then you can call that method on componentDidMount as well as on each value property change (in our case - onChange event).
import React, { Component } from "react";
class Data extends Component {
constructor(props) {
super(props);
this.state = {
items: {},
value: "America/Chicago",
isLoaded: false
};
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
const { value } = this.state;
this.fetchData(value);
}
handleChange(event) {
const value = event.target.value;
this.setState({
value
});
this.fetchData(value);
}
render() {
const { isLoaded, value, items } = this.state;
if (!isLoaded) {
return <div>Loading...</div>;
}
return (
<div>
<select onChange={this.handleChange} value={value}>
<option value="America/Chicago">Chicago</option>
<option value="America/Sao_Paulo">São Paulo</option>
</select>
{JSON.stringify(items)}
</div>
);
}
fetchData(value) {
fetch(
`https://api.timezonedb.com/v2.1/get-time-zone?key=J9X3EOT2EM8U&format=json&by=zone&zone=${value}`
)
.then(res => res.json())
.then(json => {
this.setState({
isLoaded: true,
items: json
});
});
}
}
Working demo: https://codesandbox.io/embed/728jnjprmq
Assuming you want to refresh the value of this.state.items when the user changes the value of the select, you can do this in the onChange. However, your code is in a few (incorrect) pieces. Let's start from the top.
First of all, you're setting the value property of state to '', so your componentDidMount function is going to see that value. I assume that's no good, so let's strip that out of componentDidMount entirely. We can move this code to the handleChange function instead, but it'll still need to be changed:
handleChange(e) {
this.setState({value: e.target.value});
fetch(`http://api.timezonedb.com/v2.1/get-time-zone?key=J9X3EOT2EM8U&format=json&by=zone&zone=${e.target.value}`)
.then(res => res.json())
.then(json => {
this.setState({
isLoaded: true,
items: json,
})
});
}
Notice my change - we can't access the value from the state, because setState is asynchronous, and so the value hasn't been updated by this point. We know the value comes from the select though.
The other thing you could do to improve this functionality is to turn the select into a controlled component. To do this, you just have to set the value of the field to be controlled by the state of this component. Since you're using an onChange listener for this, it makes the field a controlled component (if you weren't using an onChange, it would be a read-only field.
The loading variable in state appears to be being used incorrectly, I'm guessing you just need to check if there's data in 'items'. I'll remove this for now, but you could come back to this.
render(){
const {isLoaded} = this.state;
if(!isLoaded) {
return <div>Loading...</div>
}
return(
<div>
<select onChange={this.handleChange}>
<option value="America/Chicago">Chicago</option>
<option value="America/Sao_Paulo">São Paulo</option>
</select>
</div>
)
}
Tomasz's code has 2 mistakes: (1) it fetches resources w/o checking if the component has been unmounted; (2) it starts the request w/o updating the UI first.
I would do the following instead:
import React, {Component} from 'react'
class Data extends Component {
constructor() {
super();
this.state = {
items: {},
value: '',
isLoaded: false
}
this._isMounted = false;
// don't forget to bind your methods
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
this._isMounted = true;
}
componentWillUnmount() {
this._isMounted = false;
}
handleChange(e) {
const value = e.target.value;
this.setState({ value }, () => {
if (!this._isMounted) return;
const url = `http://api.timezonedb.com/v2.1/get-time-zone?key=J9X3EOT2EM8U&format=json&by=zone&zone=${value}`
fetch(url).then((res) => {
if (!this._isMounted) return;
const data = res.json();
this.setState({ isLoaded: true, items: data });
})
});
}
render(){
const { isLoaded } = this.state;
if(!isLoaded) {
return <div>Loading...</div>
}
return(
<div>
<select onChange={this.handleChange}>
<option value="America/Chicago">Chicago</option>
<option value="America/Sao_Paulo">São Paulo</option>
</select>
</div>
)
}
}

React - controlled component issue

So I'm working on a controlled component for a unit converter - the problem is the output is a step behind what I want it to be (e.g if I type "100" it will give the value of 10kg in lbs, "1000" -> 100 etc.). I'm sure the fix is obvious but I can't see it myself - I'm still a React newbie!
import React, { Component } from 'react';
class SearchBar extends Component {
constructor(){
super();
this.state = {
kg: '',
lbs: '',
value: 'kgtolbs',
userInput: ''
}
this.convertKgToLbs.bind(this);
this.convertLbsToKg.bind(this);
this.handleOption.bind(this);
this.handleChange.bind(this);
}
handleOption(event){
const selectedValue = event.target.value;
this.setState({
value: selectedValue
});
}
handleChange(event){
this.setState({
userInput: event.target.value
})
if(this.state.value === 'kgtolbs'){
this.convertKgToLbs();
} else {
this.convertLbsToKg();
}
}
convertKgToLbs () {
const kg = this.state.userInput;
const lbsConversion = kg * 2.205;
this.setState({
lbs: lbsConversion.toFixed(1)
});
}
render(
){
return(
<div className="search-bar">
<input className="input" type="number" onChange={this.handleChange.bind(this)} />
<select onChange={this.handleOption.bind(this)}>
<option value="kgtolbs">Kg to Lbs</option>
<option value="lbstokg">Lbs to kg</option>
</select>
<p>{this.state.lbs} </p>
<p>{this.state.kg} </p>
</div>
);
}
}
export default SearchBar;
Any help appreciated - thanks !
setState is an asynchronous function. This means that in your handleChange function when you use this.state.value, the state has not actually been updated yet. setState has a callback handler to ensure do stuff after it has ran. In your case, you can do this:
handleChange(event){
this.setState({
userInput: event.target.value
}, function() {
if(this.state.value === 'kgtolbs'){
this.convertKgToLbs();
} else {
this.convertLbsToKg();
}
});
}

How to check form is valid or not in react + material?

Is there any way to know that form is valid or not in react + material ui .I am using react material in my demo .I have three field in my form all are required . I want to check on submit button that form is valid or not
Here is my code
https://codesandbox.io/s/w7w68vpjj7
I don't want to use any plugin
submitButtonHandler = () => {
console.log("error");
console.log(this.state.form);
};
render() {
const { classes } = this.props,
{ form } = this.state;
return (
<div className={classes.searchUser__block}>
<SearchForm
handleInput={this.handleInputFieldChange}
submitClick={this.submitButtonHandler}
form={form}
/>
</div>
);
}
You would have to manually do that verification if you don't want to use any library. Material-ui does not have any validation built in as per their documentation. BUT it does give you some tools for that like errorMessage to text fields for example. You just have to play with it
Example:
class PhoneField extends Component
constructor(props) {
super(props)
this.state = { errorText: '', value: props.value }
}
onChange(event) {
if (event.target.value.match(phoneRegex)) {
this.setState({ errorText: '' })
} else {
this.setState({ errorText: 'Invalid format: ###-###-####' })
}
}
render() {
return (
<TextField hintText="Phone"
floatingLabelText="Phone"
name="phone"
errorText= {this.state.errorText}
onChange={this.onChange.bind(this)}
/>
)
}
}
a bit outdated example i had laying around
Form validation can be pretty complex, so I'm pretty sure you'll end up using a library. As for now, to answer your question, we need to think about form submission flow. Here is a simple example:
"Pre-submit"
Set isSubmitting to true
Proceed to "Validation"
"Validation"
Run all field-level validations using validationRules
Are there any errors?
Yes: Abort submission. Set errors, set isSubmitting to false
No: Proceed to "Submission"
"Submission"
Proceed with running your submission handler (i.e.onSubmit or handleSubmit)
Set isSubmitting to false
And some minimal implementation would be something like:
// ...imports
import validateForm from "../helpers/validateForm";
import styles from "./styles";
import validationRules from "./validationRules";
const propTypes = {
onSubmit: PropTypes.func.isRequired,
onSubmitError: PropTypes.func.isRequired,
initialValues: PropTypes.shape({
searchValue: PropTypes.string,
circle: PropTypes.string,
searchCriteria: PropTypes.string
})
};
const defaultProps = {
initialValues: {}
};
class SearchForm extends Component {
constructor(props) {
super(props);
this.validateForm = validateForm.bind(this);
this.state = {
isSubmitting: false,
values: {
searchValue: props.initialValues.searchValue || "",
circle: props.initialValues.circle || "",
searchCriteria: props.initialValues.searchCriteria || ""
},
...this.initialErrorState
};
}
get hasErrors() {
return !!(
this.state.searchValueError ||
this.state.circleError ||
this.state.searchCriteriaError
);
}
get initialErrorState() {
return {
searchValueError: null,
circleError: null,
searchCriteriaError: null
};
}
handleBeforeSubmit = () => {
this.validate(this.onValidationSuccess);
};
validate = (onSuccess = () => {}) => {
this.clearErrors();
this.validateForm(validationRules)
.then(onSuccess)
.catch(this.onValidationError);
};
onValidationSuccess = () => {
this.setState({ isSubmitting: true });
this.props
.onSubmit(this.state.values)
.catch(this.props.onSubmitError)
.finally(() => this.setState({ isSubmitting: false }));
};
onValidationError = errors => {
this.setState({ ...errors });
};
clearErrors = () => {
this.setState({ ...this.initialErrorState });
};
updateFormValue = fieldName => event => {
this.setState(
{
values: { ...this.state.values, [fieldName]: event.target.value }
},
() => this.validate()
);
};
render() {
// ...
}
}
SearchForm.propTypes = propTypes;
SearchForm.defaultProps = defaultProps;
export default withStyles(styles)(SearchForm);
As you can see, if submission flow will grow larger (for example touching inputs, passing errors, etc), the of amount of complexity inside of a component will significantly grow as well. That is why it's more preferable to use a well-maintained library of choice. Formik is my personal preference at the moment.
Feel free to check out updated codesandbox. Hope it helps.
Hi Joy I've made desirable form validation if required fields are empty.
Here is the updated codesandbox: https://codesandbox.io/s/50kpk7ovz4

Resources