How to handle multiple controlled inputs with react es6? - javascript

Here's my fiddle
https://codepen.io/seunlanlege/pen/XjvgPJ?editors=0011
I have two inputs and I'm trying to use one method to handle the onChange event for any input field.
I've torn the internet apart looking for a solution but came up with nothing.
I'm using es6 please how do I go about this?
class Form extends React.Component {
`constructor(props) {
super(props);
this.state = {text:{
e:'hi',
c:''
}};
this.handleSubmit = this.handleSubmit.bind(this);
}`
`handleChange(event,property) {
const text = this.state.text;
text[property] = event.target.value;
this.setState({text});
}`
`handleSubmit(event) {
alert('Text field value is: ' + this.state.text.e);
}`
`render() {
return (
<div>
<div>{this.state.text.e}</div>
<input type="text"
placeholder="Hello!"
value={this.state.text.e}
onChange={this.handleChange.bind(this)} />
<input type="text"
placeholder="Hello!"
value={this.state.text.c}
onChange={this.handleChange.bind(this)} />
<button onClick={this.handleSubmit}>
Submit
</button>
</div>
);
}
}`
ReactDOM.render(
`<Form />`,
document.getElementById('root')
);

You have not passed the propert to the handeChange function. pass it like this.handleChange.bind(this, 'e') and also the order of receiving props is wrong, property will be the first argument and then the event and not the reverse.
Code:
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {text:{
e:'hi',
c:''
}};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(property, event) {
console.log(event.target.value);
const text = {...this.state.text};
text[property] = event.target.value;
this.setState({ text }); //or you can use the shorthand here. ES6 is awesome <3
}
handleSubmit(event) {
alert('Text field value is: ' + this.state.text.e);
}
render() {
return (
<div>
<div>{this.state.text.e}</div>
<div>{this.state.text.c}</div>
<input type="text"
placeholder="Hello!"
value={this.state.text.e}
onChange={this.handleChange.bind(this, 'e')} />
<input type="text"
placeholder="Hello!"
value={this.state.text.c}
onChange={this.handleChange.bind(this, 'c')} />
<button onClick={this.handleSubmit}>
Submit
</button>
</div>
);
}
}
ReactDOM.render(
<Form />,
document.getElementById('root')
);
CodePen

One way to do this would be to give each of your inputs a name attribute and set the state based on that:
class Form extends React.Component {
constructor(props) {
super(props);
this.state = { text: {
e: 'hi',
c: ''
} };
this.onChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
var oldState = this.state.text;
var newState = { [e.target.name]: e.target.value };
// I have to assign/join because you've put the text state in a parent object.
this.setState({ text: Object.assign(oldState, newState) });
}
handleSubmit(event) {
alert('Text field value is: ' + this.state.text.e);
}
render() {
console.log(this.state);
return (
<div>
<div>{this.state.text.e}</div>
<input type="text"
placeholder="Hello!"
name="e"
value={this.state.text.e}
onChange={this.onChange} />
<input type="text"
placeholder="Hello!"
name="c"
value={this.state.text.c}
onChange={this.onChange} />
<button onClick={this.handleSubmit}>
Submit
</button>
</div>
);
}
}
ReactDOM.render(
<Form />,
document.getElementById('View')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.0/react-dom.min.js"></script>
<div id="View"></div>
Also, there are so-called two-way binding helpers. As I understand they still show mixins in React's documentation, so you are probably better off with third party libraries like react-link-state:
this.state = {
username: '',
password: '',
toggle: false
};
<input type="text" valueLink={linkState(this, 'username')} />
<input type="password" valueLink={linkState(this, 'password')} />
<input type="checkbox" checkedLink={linkState(this, 'toggle')} />

Related

how can I use state for multiple value?

I am trying to use two states in my Add Customer JS one is used to hide the form and the second is used for JSON.
I want to use form-State to hide a form on cancel button click and the initial-State for JSON.
I want to do something like this
Is it possible to have two states in one react component
import React from 'react';
import { Button, Form, Modal } from 'semantic-ui-react';
export default class AddCustomer extends React.Component {
constructor(props) {
super(props);
this.state = {
showCreateForm:false,
formData:{
name: '',
address: ''
}
}
this.handleChangeName = this.handleChangeName.bind(this);
this.handleChangeAddress = this.handleChangeAddress.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChangeName(event) {
const value = event.target.value;
console.log(value);
this.setState({formData:{name:value}});
//name: ""
//address: ""
console.log(this.state.formData);
}
handleChangeAddress(event) {
const value = event.target.value;
console.log(value);
this.setState({formData:{address:value}});
//name: "ram" but now there is no address in formData
console.log(this.state.formData);
}
handleSubmit(event) {
event.preventDefault();
////address: "aaaaa" now there no name in formData
console.log(this.state.formData);
this.setState({formData:{
name:this.state.name, address:this.state.address
}});
this.props.onAddFormSubmit(this.state.formData);
}
//On cancel button click close Create user form
closeCreateForm = () => {
this.setState({ showCreateForm: false })
}
//Open Create new Customer form
openCreateCustomer = () => {
this.setState({ showCreateForm: true })
}
render() {
return (
<div>
<Modal closeOnTriggerMouseLeave={false} trigger={
<Button color='blue' onClick={this.openCreateCustomer}>
New Customer
</Button>
} open={this.state.showCreateForm}>
<Modal.Header>
Create customer
</Modal.Header>
<Modal.Content>
<Form onSubmit={this.handleSubmit}>
<Form.Field>
<label>Name</label>
<input type="text" placeholder ='Name' name = "name"
value = {this.state.name}
onChange = {this.handleChangeName}/>
</Form.Field>
<Form.Field>
<label>Address</label>
<input type="text" placeholder ='Address' name = "address"
value = {this.state.address}
onChange = {this.handleChangeAddress}/>
</Form.Field>
<br/>
<Button type='submit' floated='right' color='green'>Create</Button>
<Button floated='right' onClick={this.closeCreateForm} color='black'>Cancel</Button>
<br/>
</Form>
</Modal.Content>
</Modal>
</div>
)
}
}
You can directly give initial state on the constructor. e.g
this.state ={showCreateForm: false, formModel:{name:'abc', address:'xyz'}}
Yes, you can have multiple state variables technically.
As it was already mentioned, yes, you could do it in the constructor. However you could go even further and declare it as a class member. Like following:
export default class Customer extends React.Component {
state = {
showCreateForm: false,
form: {
name: "",
address: "",
}
}
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
this.props.onAddFormSubmit(this.state.form);
this.setState({
...this.state,
form: {
name: "",
address: "",
}
});
}
// ...
render() {
return (
<div>
<Modal
closeOnTriggerMouseLeave={false}
trigger={
<Button color="blue" onClick={this.openCreateCustomer}>
New Customer
</Button>
}
open={this.state.showCreateForm}
>
<Modal.Header>Create customer</Modal.Header>
<Modal.Content>
<Form onSubmit={this.handleSubmit}>
<Form.Field>
<label>Name</label>
<input
type="text"
placeholder="Name"
name="name"
value={this.state.form.name}
onChange={this.handleChange}
/>
</Form.Field>
<Form.Field>
<label>Address</label>
<input
type="text"
placeholder="Address"
name="address"
value={this.state.form.address}
onChange={this.handleChange}
/>
</Form.Field>
<br />
<Button type="submit" floated="right" color="green">
Create
</Button>
<Button
floated="right"
onClick={this.closeCreateForm}
color="black"
>
Cancel
</Button>
<br />
</Form>
</Modal.Content>
</Modal>
</div>
);
}
}

handleSubmit function appears to be refreshing page without firing any internal logic

The handleSubmit function seems to refresh the page without firing any of the internal logic. I've set up a few console.log's along the way to test out if the internally declared const that's set to the venue property in the state would log, but nothing appears.
I've commented on various parts of the function stepwise starting with setting the scheduling variable to my Firebase schedule table.
After that, I changed the handleSubmit function from an arrow function to just handleSubmit(e) (sorry, I'm new to this so I'm not familiar with the terminology)
import React, {Component} from 'react';
import FrontNav from './nav.js';
import firebase from '../Firebase';
class FrontSchedule extends Component {
constructor () {
super();
this.state = {
venue:'',
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange = (e) => {
e.preventDefault();
this.setState ({
venue: e.target.value,
});
}
handleSubmit = (e) => {
e.preventDefault();
// let schedule = firebase.database().ref('schedule')
const item = {
venue: this.state.venue,
}
console.log(item);
// schedule.push(item);
// console.log(firebase.database().ref('schedule'));
console.log(this.state.venue);
// this.setState({
// venue:'',
// })
}
render(){
return(
<div>
<FrontNav/>
<h1>Schedule</h1>
<form>
<input type="text"
name="venue"
onChange={this.handleChange}
onSubmit={this.handleSubmit}
value={this.state.venue}/>
<button onSubmit={this.handleSubmit}> Enter Event </button>
</form>
</div>
);
}
}
export default FrontSchedule;
Herein lies the crux of the problem. The page refreshes and the input bar clears, but no error message is provided. At this point, I'm really confused about what is going on here. Any feedback is appreciated!
Let us consider the following example:-
<form>
<label>
Name:
<input type="text" name="name" />
</label>
<input type="submit" value="Submit" />
</form>
Now, when we press on submit button the default behavior to browse to a new page. If you want this behavior it works out of the box in ReactJS. But in cases where you need more sophisticated behavior like form validations, custom logic after the form is submitted you can use controlled components.
We can do so by using following:-
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
Now coming to your solution it can be implemented as follows:-
class FrontSchedule extends React.Component {
constructor () {
super();
this.state = {
venue:'',
}
/* this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this); */
}
handleChange = (e) => {
e.preventDefault();
this.setState ({
venue: e.target.value,
});
}
handleSubmit = (e) => {
event.preventDefault();
// let schedule = firebase.database().ref('schedule')
const item = {
venue: this.state.venue,
}
console.log(item);
// schedule.push(item);
// console.log(firebase.database().ref('schedule'));
console.log(this.state.venue);
// this.setState({
// venue:'',
// })
}
render(){
console.log(this.state);
return(
<div>
<h1>Schedule</h1>
<form onSubmit={this.handleSubmit}>
<input type="text"
name="venue"
onChange={this.handleChange}
onSubmit={this.handleSubmit}
value={this.state.venue}/>
<input type="submit" value="Submit"/>
</form>
</div>
);
}
}
ReactDOM.render(<FrontSchedule />, document.querySelector("#app"))
Hope it helps :)
You can read more at react documentationhere

How can I keep on adding username and status instead of updating the previous one in reactJS?

I have two components. One named 'Adduser' containing form elements so that a user may add details of post. Other named 'PostAdded', in which i want to show all posts in a list item. On every click, I want 'Adduser' to grab data from input elements and pass it to 'PostAdded' in a way that 'PostAdded' show every individual post(title and post together) in a new div instead of updating previous one. What is the best approach to do it?
File 'Adduser.js'
class AddUser extends Component {
constructor(props) {
super();
this.state = {
title : "",
post : "",
}
this.handleclick = this.handleclick.bind(this);
}
handleclick() {
this.setState(prevState => ({
title : document.getElementById("title").value,
post : document.getElementById("post").value,
}));
}
render() {
return(
<div>
<input type="text" id="title" placeholder="Title here" />
<input type="text" id="post" placeholder="Post here" />
<input type="button" onClick={this.handleclick} value="Add Post" />
<PostAdded posts={this.state.post} />
</div>
)
}
}
export default AddUser;
File 'PostAdded.js'
import React, {Component} from 'react';
class PostAdded extends Component {
constructor(props) {
super();
}
render() {
return <ul>
{ this.props.posts.map(post =>
<li>{post}</li>
)}
</ul>
}
}
export default PostAdded;
In AddUser component change your state and handleclick method. I have not modified your code too much so you can understand it easily.
class AddUser extends Component {
constructor(props) {
super();
this.state = {
posts: [],
}
this.handleclick = this.handleclick.bind(this);
}
handleclick() {
// accessing values from the input
let title = document.getElementById("title").value
let post = document.getElementById("post").value
// creating a new object
let newPostObj = {title, post}
// concatenating new object to component posts state
let newPost = this.state.posts.concat(newPostObj)
// setting newPost as component new state
this.setState({
posts: newPost
})
// emptying the input fields
document.getElementById("title").value = ''
document.getElementById("post").value = ''
}
render() {
return(
<div>
<input type="text" id="title" placeholder="Title here" />
<input type="text" id="post" placeholder="Post here" />
<input type="button" onClick={this.handleclick} value="Add Post" />
<PostAdded posts={this.state.posts} />
</div>
)
}
}
In your PostAdded component update render() method
class PostAdded extends Component {
constructor(props) {
super();
}
render() {
return (
<ul>
{ this.props.posts.map((post, i) =>
<li key={`${i}-post`}><span>{post.title}</span><span>{post.post}</span></li>
)}
</ul>
)
}
}
UPDATE
Change your AddUser Component
class AddUser extends Component {
constructor(props) {
super();
this.state = {
posts: [],
title: '',
post: ''
}
this.handleClick = this.handleClick.bind(this);
this.handleChange = this.handleChange.bind(this);
}
// called when we type something in input fields
handleChange(e) {
// you can console log here to see e.target.name and e.target.value
this.setState({
[e.target.name]: e.target.value
})
}
handleClick() {
// using spread operator to copy previous state posts and adding new post object
let newPosts = [ ...this.state.posts, { title: this.state.title, post: this.state.post}]
this.setState({
posts: newPosts,
title: '',
post: ''
})
}
render() {
return(
<div>
// added name,value attributes and onChange listener
<input type="text" name="title" value={this.state.title} onChange={this.handleChange} placeholder="Title here" />
<input type="text" name="post" value={this.state.post} onChange={this.handleChange} placeholder="Post here" />
<input type="button" onClick={this.handleClick} value="Add Post" />
<PostAdded posts={this.state.posts} />
</div>
)
}
}

React: Render component based on click

I currently have an input field set up so the inputs value will be console logged when a button is pressed, I am trying to render a component instead of this console.log.
I can get it to sorta work but it re-renders every time I type a single character because I'm not sure how to check for the click within the render method, I read through some of the docs but couldn't figure out how do to it. How could I go about achieving this?
here is the code
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
input: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({input: e.target.value});
}
handleSubmit(e) {
e.preventDefault();
console.log(this.state.input)
}
render() {
//Not sure how to check for this
if (this.state.input) {
return <Fetch username={this.state.input} />
}
return(
<form>
<label>
Name:
<input type="text" value={this.state.input} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" onClick={this.handleSubmit} />
</form>
);
}
}
let me know if you need anymore information. Thanks!
Edit: By the way, I want to be able to submit the form many times, sorry, I should have been more descriptive of my problem. I want the Form component to stay rendered and I want the Fetch component to get rendered on clicked
A solution depends on your needs and there might be a lot of them. The simplest way, just to demonstrate:
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
input: '',
submitted: false, // adding the flag
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({input: e.target.value});
}
handleSubmit(e) {
e.preventDefault();
this.setState({ submitted: true }); // set the flag on submit
console.log(this.state.input)
}
render() {
//Not sure how to check for this
if (this.state.submitted) { // show the component by the flag
return <Fetch username={this.state.input} />
}
return(
<form>
<label>
Name:
<input type="text" value={this.state.input} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" onClick={this.handleSubmit} />
</form>
);
}
}
Obviously, in real application it's too simple logic. You have to define first, what are you trying to reach.
UPDATE:
If you want to keep the input field and the component simultaneously, you can do it like this:
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
input: '',
submitted: false, // adding the flag
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({input: e.target.value});
}
handleSubmit(e) {
e.preventDefault();
this.setState({ submitted: true }); // set the flag on submit
console.log(this.state.input)
}
render() {
return(
<form>
<label>
Name:
<input type="text" value={this.state.input} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" onClick={this.handleSubmit} />
{this.state.submitted && (
<Fetch username={this.state.input} />
)}
</form>
);
}
}
Actually what you are doing is every time the user type a character, you are displaying Fetch component with the input's value passed in props.
With this and with the handleChange method
if (this.state.input) {
return <Fetch username={this.state.input} />
}
The condition here return true if input is different from "". So when a character is type it becomes true and displays it.
In your case what you want to do is to display the Fetch component when you are clicking on the submit button.
What you can do is create a state to manage it.
For example you could have this in your state :
this.state = {
input: '',
fetchIsVisibile: false,
};
Then in your handleSubmit just do this:
handleSubmit(e) {
this.setState({ fetchIsVisible: true }),;
e.preventDefault();
}
And the condition for display your fetch component would be using this new state instead of input state
if (this.state.fetchIsVisible) {
return <Fetch username={this.state.input} />
}
You can add state whether the form is submitted in the state and to check for it when rendering the component
if you want to check whether the form is submitted and the input is not empty you can use (this.state.submitted && this.state.input) instead
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
input: '',
submitted: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({input: e.target.value});
}
handleSubmit(e) {
this.setState({
submitted: true
});
}
render() {
//Not sure how to check for this
if (this.state.submitted) {
return <div>
<div>FETCH COMPONENT</div>
<div>input: {this.state.input}</div>
</div>
}
return(
<form>
<label>
Name:
<input type="text" value={this.state.input} onChange={this.handleChange} />
</label>
<div>input: {this.state.input}</div>
<input type="submit" value="Submit" onClick={this.handleSubmit} />
</form>
);
}
}
ReactDOM.render(<Form />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

How to avoid duplicate event listener in react?

I have a form in react with many input components. I do not like that I have to write a new onChange handler method for every input component that I build. So I want to know how can I stop repeated code.
<Input
label={"Blog Name"}
hint={"e.g. 'The Blog'"}
type={"text"}
value={this.state.name}
onChange={this.handleInputChange.bind(this, "name")}
/>
<Input
label={"Blog Description"}
hint={"e.g. 'The Blog Description'"}
type={"text"}
value={this.state.desc}
onChange={this.handleInputChange.bind(this, "desc")}
/>
So instead of writing a new function I am reusing the same function and passing an extra value. Is this the right way to do it? How do other experienced people solve this problem.
If you want your parent component to maintain the state with the value of each input field present in 'Input' child components, then you can achieve this with a single change handler in the following way:
handleChange(id, value) {
this.setState({
[id]: value
});
}
where the id and value are obtained from the Input component.
Here is a demo: http://codepen.io/PiotrBerebecki/pen/rrJXjK and the full code:
class App extends React.Component {
constructor() {
super();
this.state = {
input1: null,
input2: null
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(id, value) {
this.setState({
[id]: value
});
}
render() {
return (
<div>
<Input id="input1"
changeHandler={this.handleChange} />
<Input id="input2"
changeHandler={this.handleChange} />
<p>See input1 in parent: {this.state.input1}</p>
<p>See input2 in parent: {this.state.input2}</p>
</div>
);
}
}
class Input extends React.Component {
constructor() {
super();
this.state = {
userInput: null
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const enteredText = event.target.valuel
this.setState({
userInput: enteredText
}, this.props.changeHandler(this.props.id, enteredText));
}
render() {
return (
<input type="text"
placeholder="input1 here..."
value={this.state.userInput}
onChange={this.handleChange} />
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
You can try event delegation, just like the traditional ways.
That is, just bind a function to the parent form element, and listens to all the events bubbling up from the children input elments.

Categories