Efficient way to init state storage of forms - reactjs

I'm working on a quite comprehensive form and was wondering if there is a smart way to prevent me of doing the following state initialisation:
class Demo extends React.Component {
state = { firstName = "",
secondName = "" };
//and so on...
render() {
const { firstName, secondName } = this.state;
//and so on
return (
<div>
<Form>
<Form.Input
placeholder="Name"
name="name"
value={firstName}
/>
//and so on
</Form>
</div>
);
}
}
If I don't init the state with empty strings I get the following Warning:
Component is changing an uncontrolled input of type text to be
controlled. Input elements should not switch from uncontrolled to
controlled (or vice versa). Decide between using a controlled or
uncontrolled input element for the lifetime of the component.
What's the state of the art way to deal with this?
Thanks!
Stefan

If you are using value as your state value then it will be undefined in render method in case you don't initialize state. So it is recommended to initialize your state like you're doing because you have controlled inputs. I believe this would help your case

Something like this should work but problem is you need input handlers for each fields which is cumbersome if you have huge form.
class Demo extends React.Component {
constructor(props) {
super(props)
this.state = {
firstName: '',
lastName: '',
submitted: false
};
}
handleFirstName = (e) => {
this.setState({firstName: e.target.value});
}
handleLastName = (e) => {
this.setState({lastName: e.target.value});
}
handleSubmit(e) {
e.preventDefault();
this.setState({ submitted: true });
const payload = [{this.state.firstName, this.state.lastName}];
this.props.saveData(payload);
}
render() {
return(
<form>
<label>
Firstname:
<input type="text" value={this.state.firstName} onChange={this.handleFirstName} />
</label>
<label>
Lastname:
<input type="text" value={this.state.lastName} onChange={this.handleLastName} />
</label>
<input type="submit" value="Submit" onClick={this.handleSubmit} />
</form>
);
}
}
Better solution for handling form is using lib like react-final-form

Related

multiple input elements in form - React

It's a simple form which updates the state object onChange and displays that state object when submitted. I was not able to get it to work when there are multiple input elements.
Can anyone tell me what's wrong in this code?
onSubmit works when there's only one input element, but not when there are multiple!
class ReactForm extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.validate = this.validate.bind(this);
this.state = {
name: "",
email: ""
};
}
handleChange(event) {
event.preventDefault();
const name = event.target.name;
const value = event.target.value;
this.setState({
[name]: value
});
}
validate(event) {
event.preventDefault();
console.log(this.state);
}
render() {
return (
<div>
<form onSubmit={this.validate}>
<div>
<input
type="text"
name="name"
value={this.state.name}
onChange={this.handleChange}
/>
<input
type="email"
name="email"
value={this.state.email}
onChange={this.handleChange}
/>
</div>
</form>
</div>
);
}
}
ReactDOM.render(
<ReactForm />,
document.getElementById("root")
);
You need to have a submit button if you have more than 1 input, you can add a hidden one if you want:
<input type="submit" hidden />
Here's a codesandbox: https://codesandbox.io/s/suspicious-almeida-e3f00
And here is the explanation in detail: Why does a FORM with one text INPUT submit on enter while one with two text INPUTs does not?
I really liked the approach of tudor.
Here is a different approach that can remove the state handling as well. But this may require polyfill for IE and Safari. You can use FormData to access the form values.
new FormData(e.target);
Here is the working sandbox link: https://codesandbox.io/s/long-wind-ybl1w
Hope this helps!
Please add an element input and button. Button should have type="submit" for submitting!
It will work!

Render user input in a different component in React

I am currently rendering user input in a div, in the same component as my form. I want to render the user input on another page (in my other component) instead. How can I render the user input in a different component of my app?
Structure of app:
App.js (navigation setup for react router)
3 components: Home.js, ProjectIdeas.js and Projects.js
Planning.js (the component which contains my form and currently renders the user input) is the child of Projects.js. Projects.js is where I want to render the user input.
Planning.js
constructor() {
super()
this.state = {
title: '',
features: '',
details: ''
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange(event) {
this.setState({
[event.target.name]: event.target.value
})
}
handleSubmit(event) {
const {
title,
features,
details
} = this.state;
event.preventDefault();
this.setState({ title, features, details });
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<div>{this.state.title}</div>
<div>
<label className="label-title">
Project Title:</label>
<input name="title" id="title" type="text" onChange={this.handleChange}/>
</div>
<div>
<label className="label-features">
Features:</label>
<input name="features" placeholder="Button (directs to contact page), black footer" id="features" type="text" onChange={this.handleChange}/>
</div>
<div>
<label className="label-details">
Other details: </label>
<input name="details" placeholder="Other Details" id="details" type="text" onChange={this.handleChange}/>
</div>
<input class="submit" type="submit" value="Save plan" />
</form>
)
}
}
Projects.js
class Projects extends React.Component {
constructor(props) {
super(props)
this.state = { isEmptyState: true }
}
triggerAddPlanState = () => {
this.setState({
...this.state,
isEmptyState: false,
isAddPlanState: true
})
}
render() {
return (
<div>
{this.state.isEmptyState && <AddPlanButton addPlan={this.triggerAddPlanState} />}
{this.state.isAddPlanState && <Planning />}
</div>
)
}
}
You'll need to store the value outside the component, either in a common parent component or somewhere orthogonal.
If the components share a nearby parent, you can have the parent keep track of the state and pass it down as props to both:
function ParentComponent () {
const [value, setValue] = React.useState();
<ComponentA value={value} onChange={v => setValue(v)} />
<ComponentB value={value} onChange={v => setValue(v)} />
}
There are many other ways to do it if your app doesn't lend itself to the above solution. I'd recommend you consider using a store like Redux or possibly look at React Context. (Your scenario doesn't seem like a good use of context but you could make it work.)
Implementing your own store is not particularly complicated. Here's an example of one way to do it.

React.js: Is there a simpler way of defining Form Input element as a best practice ? Without using another library or extension

After having gone thru a few docs & books on React, I understand that, for an Html Form input element that could be defined as:
<form>
<label>
Name:
<input type="text" name="name" />
</label>
</form>
the recommended best practice in React is to use the "Controlled component" approach with an equivalent code like the following Sample from React Forms doc:
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
} //constructor
handleChange(event) {
this.setState({value: event.target.value});
} // handleChange
render() {
return (
<form>
<label>
Name:
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
</label>
</form>
);
} //render
} // class
Is there a simpler alternative in React itself (without adding additional libraries or extensions) ? Please excuse, if I missed something basic.
That's the simplest approach. It's also correct.
One way to further simplify is to give the onChange an anonymous arrow function.
<form>
<label>
Name:
<input
type="text"
value={this.state.value}
onChange={e => this.setState({ value: e.target.value })}
/>
</label>
</form>
But no advantage here really. Your approach makes it more readable.
But there is a more simple way to initialize state.
It's like this:
class NameForm extends React.Component {
state = { value: "" }
....
}
And if you make class methods arrow functions you don't have to bind them:
handleChange = (event) => { .... }

React. Can I get rid of ref in favor of onChange?

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.

Is there a neater way to connect an input field to a state property in React than onChange?

So look at this code below for our example, a simple 2-way data-binding on an input field connecting the field to a property inputValue.
But say you have a more complex page with 30 or more inputs. Are you supposed to write 30+ onChange handlers in the class, all with different names corresponding to the inputs like onNameChange, onEmailChange, onPhoneChange, and so on? Is there no neater, more implicit way to bind inputs than what I have below here?
React.createClass({
getInitialState() {
inputValue: ''
},
render() {
return (
<input
type='text'
value={this.state.inputValue}
onChange={this.onChange} />
);
},
onChange(e) {
this.setState({ inputValue: e.target.value });
}
});
Edit: I suppose I could do this and avoid writing handlers on the class:
<input onChange={ e => this.setState({firstName: e.target.value}) } />
Is that kosher?
React docs has your solution:
https://facebook.github.io/react/docs/forms.html#handling-multiple-inputs
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleInputChange(event) {
const target = event.target;
this.setState({
[target.name]: target.value
});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input name="name" type="text" value={this.state.name} onChange={this.handleInputChange} />
</label>
<label>
Email:
<input name="email" type="text" value={this.state.email} onChange={this.handleInputChange} />
</label>
<label>
Pet:
<input name="country" type="text" value={this.state.country} onChange={this.handleInputChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
The neater way would be to have a single onChange handler and pass on the id to it, and store the value with that id. Your solution will look like
React.createClass({
getInitialState() {
},
onChange(e, type) {
this.setState({[type]: e.target.value})
},
render() {
return (
<input
type='text'
value={(this.state.inputValue)? this.state.inputValue: ''}
onChange={this.onChange.bind(this, 'inputValue')} />
<input
type='text'
value={(this.state.emailValue)? this.state.emailValue: ''}
onChange={this.onChange.bind(this, 'emailValue')} />
);
},
onChange(e) {
this.setState({ inputValue: e.target.value });
}
});
The value is given with a ternary operator expression because initially the state is not defined and hence we will get a warning that input is trying to change the uncontrolled input to controlled.
The other way is to have the handler inline like
onChange={ e => this.setState({inputValue: e.target.value}) }
but say you have 30 inputs you need to define their initial state and let it set its value like above, only difference being the value being assigned to it should be with the expression as shown in the first example
The other way to assign value to the input as #MayankShukla suggessted will be
value = {this.state.inputValue || ''}
with getInitialState looking like
getInitialState() {
return {}
},

Resources