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>
);
}
}
Related
After typing in a team name, I want react to redirect us to the specified page (ie: "teams/this.state.searchText" w/ search text being what the user has typed into the search form). I get a re-render that does nothing/does no redirecting... Can this be done with reacts new v4 Redirect component?
export default class Nav extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
searchText: ''
}
this.submit = this.submit.bind(this);
}
onSearchChange = e => {
console.log(e.target.value)
this.setState({ searchText: e.target.value });
}
submit = e => {
e.preventDefault();
// with the new search state from above, get the state and perform a search with it to local/team/"searchValue"
e.currentTarget.reset();
}
redirectIt = () => {
this.props.history.push(`teams/${this.state.searchText}`)
}
render() {
return (
<Navbar className="bg-light justify-content-between">
<Form onSubmit={this.submit} >
<FormControl type="text" placeholder="Search Team" className=" mr-sm-2" onChange={this.onSearchChange} />
<Button type="submit">Submit</Button>
</Form >
<div className='logo'>NHL</div>
<Form inline>
<Button type="submit" onClick={this.redirectIt}>Login</Button>
</Form>
</Navbar>
);
}
}
With Redirect, it would look something like this. you could basically tell the browser to go to a different page
import { Redirect } from 'react-router-dom'
export default class Nav extends React.Component {
constructor(props) {
super(props);
this.state = {
searchText: '',
isRedirected: false
}
}
onSearchChange = e => {
console.log(e.target.value)
this.setState({ searchText: e.target.value });
}
submit = e => {
e.preventDefault();
// with the new search state from above, get the state and perform a search with it to local/team/"searchValue"
e.currentTarget.reset();
}
redirectIt = () => {
this.setState({isRedirected: true})
}
render() {
// change the to prop to the next component
if (this.state.isRedirected) return <Redirect to=`/teams/${this.state.searchText}` />
return (
<Navbar className="bg-light justify-content-between">
<Form onSubmit={this.submit}>
<FormControl type="text" placeholder="Search Team" className=" mr-sm-2" onChange={this.onSearchChange} />
<Button type="submit">Submit</Button>
</Form >
<div className='logo'>NHL</div>
<Button onClick={this.redirectIt}>Login</Button>
</Navbar>
);
}
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>
)
}
}
I want create a function using with i can reset value in form inputs without submit. I tried create that function in App Component (resetFormFields) and pass it on props to Form Component. It's preety simply when I want to do this onSubmit (e.target.reset()) but I got stuck when I have to do it without submit, on a different element than the form. Can I do that without adding these values to state?
App:
class App extends Component {
state = {
people: [],
formMessages: [],
person: null
};
handleFormSubmit = e => {
e.preventDefault();
const form = e.target;
const name = form.elements["name"].value;
const username = form.elements["username"].value;
this.addPerson(name, email);
form.reset();
};
resetFormFields = () => {
return;
}
render() {
return (
<div className="App">
<Form formSubmit={this.handleFormSubmit}
reset={this.resetFormFields} />
</div>
);
}
Form:
const Form = props => (
<form className={classes.Form}
id="form"
onSubmit={props.formSubmit}>
<input autoFocus
id="name"
type="text"
defaultValue=""
placeholder="Name..."
/>
<input
id="email"
type="text"
defaultValue=""
placeholder="Email..."
/>
<Button
btnType="Submit"
form="form"
type='submit'>
Submit
</Button>
<label onClick={props.reset}>Reset fields</label>
</form> );
onHandleFormSubmit = (e) =>{
e.preventDefault();
e.target.reset();
}
You need to make your inputs controlled by passing the value you store in your state then you just have to reset the state values and your component value resets.
check this sample below
handleInputChange = (e) => {
let { name, value } = e.target;
this.setState({
...this.state,
inputs: {
[name]: value
}
});
}
your component will now look like
<input name='fullName' value={this.state.inputs.fullName} onChange={this.handleInputChange} />
Your reset function will just clear the state and your input field will be empty since it's controlled via state
resetInputFields = () => {
this.setState({ inputs: {} })
}
you should give set your input values based on component state, then just update the component state
class App extends Component {
state = {
people: [],
formMessages: [],
person: null,
name: "",
email: "",
};
updateState = (newState) => {
this.setState(newState);
}
handleFormSubmit = e => {
e.preventDefault();
this.addPerson(this.state.name, this.state.email);
form.reset();
};
resetFormFields = () => {
this.setState({name:"", email: ""});
}
render() {
return (
<div className="App">
<Form formSubmit={this.handleFormSubmit} updateState={this.updateState}
reset={this.resetFormFields} email={this.state.email} name={this.state.name} />
</div>
);
}
and then
const Form = props => (
<form className={classes.Form}
id="form"
onSubmit={props.formSubmit}>
<input autoFocus
id="name"
type="text"
defaultValue=""
value={this.props.name}
onChange={(e) => this.props.updateState({name: e.target.value})}
placeholder="Name..."
/>
<input
id="email"
type="text"
defaultValue=""
value={this.props.email}
onChange={(e) => this.props.updateState({email: e.target.value})}
placeholder="Email..."
/>
<Button
btnType="Submit"
form="form"
type='submit'>
Submit
</Button>
<label onClick={props.reset}>Reset fields</label>
</form> );
I have a field Location, that is a mandatory field for yhe API, so can't be submitted blank. So I am trying to set 0 as initialValue for the field. the Location field is on the second step of the form and setting initialValues on WizardFormSecondPage removes all input previous input data from the state. How do I set the initialValue for the Location field and keep all my data put in the first step?
Location component:
export class GetLocation extends Component{
constructor(){
super();
this.getMyLocation = this.getMyLocation.bind(this);
}
getMyLocation = () => {
const location = window.navigator && window.navigator.geolocation;
if (location) {
location.getCurrentPosition((position) => {
this.props.onLocationChanged(position.coords);
},
(positionError) => {
console.log(positionError.message);
this.props.onLocationChanged("0")
},{maximumAge:0, timeout: 60000})
} else {
console.log();
this.props.onLocationChanged("0")
}
};
render(){
return(
<div>
<p>Your location is </p>
<Field
name="latitude"
component="input"
className="form-control" initialValues={0.0}
/>
<Field
name="longitude"
component="input"
className="form-control"
/><br/>
<button type="button" className="btn btn-success" onClick={this.getMyLocation.bind(this)}>Get Geolocation</button>
</div>
);
}
}
WizardFormSecondPage
let WizardFormSecondPage = props => {
const { handleSubmit, previousPage} = props;
const onLocationChanged = (loc) => {
props.change('location.latitude', loc.latitude);
props.change("location.longitude", loc.longitude);
};
return (
<form onSubmit={handleSubmit} className="form-horizontal">
<div className="panel">
<div className="form-group">
<label className="control-label col-sm-2" htmlFor="address">
Location
</label>
<div className="row">
<div className="col-sm-12">
<p className="label-lead">Own Address</p>
<FormSection name="location" component={Address}>
<Address />
</FormSection>
<p className="label-lead">Location Coordinates</p>
<FormSection name="location" component={GetLocation}>
<GetLocation onLocationChanged={onLocationChanged} />
</FormSection>
</div>
</div>
</div>
</div>
<div className="clearfix">
<button type="button" className="previous pull-left btn btn-default" onClick={previousPage}>
Previous
</button>
<button type="submit" className="next pull-right btn btn-primary">
Next
</button>
</div>
</div>
</form>
);
};
export default reduxForm({
form: "wizard", // <------ same form name
destroyOnUnmount: false, // <------ preserve form data
forceUnregisterOnUnmount: true, // <------ unregister fields on unmount
validate
})(WizardFormSecondPage);
Any help is much appreciated.
Turns out, my approach was wrong. I could set an initialValue to the entire WizardForm and it would not initialize again. So, in stead of trying to set initialize WizardFormSecondPage, I had to set values on WizardForm.js. Here's my WizardForm.js:
class WizardForm extends Component {
constructor(props) {
super(props);
this.nextPage = this.nextPage.bind(this);
this.previousPage = this.previousPage.bind(this);
this.state = {
page: 1,
};
}
nextPage() {
this.setState({ page: this.state.page + 1 });
}
previousPage() {
this.setState({ page: this.state.page - 1 });
}
onSubmit(values, dispatch) {
return dispatch(saveData(values));
// Call the action creator which is responsible for saving data here.
}
render() {
const { onSubmit } = this.props;
const { page } = this.state;
return (
<div>
{page === 1 && <WizardFormFirstPage onSubmit={this.nextPage} />}
{page === 2 &&
<WizardFormSecondPage
previousPage={this.previousPage}
onSubmit={this.nextPage}
/>}
{page === 3 &&
<WizardFormPreview
previousPage={this.previousPage}
onSubmit={this.onSubmit}
/>}
</div>
);
}
}
WizardForm.propTypes = {
onSubmit: PropTypes.func.isRequired,
};
// this part sets the initial values. connect if you need store.
WizardForm = reduxForm ({
form: 'wizard',
initialValues: {
location: {
latitude: "0.0",
longitude: "0.0"
}
}
})(WizardForm)
export default WizardForm;
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')} />