I have the following code, it basically accepts some basic input and when submit button is clicked user is notified with an alert, state is constantly being updated via onChange event. What i wonder is can i somehow pass the retrieved data to another component inside the event handler for submit button (which i have called handleFormSubmit)? I have recently seen react has something called 'context' ...maybe that would be best here? Advice please? :)
class Form extends Component {
constructor(props) {
super(props)
this.state = {
username: '',
comments: '',
topic: 'react'
}
this.handleUsernameChange = this.handleUsernameChange.bind(this);
this.handleCommentsChange = this.handleCommentsChange.bind(this);
this.handleTopicChange = this.handleTopicChange.bind(this);
this.handleFormSubmit = this.handleFormSubmit.bind(this);
}
handleUsernameChange(event){
this.setState({
username: event.target.value
},
() =>{
console.log(this.state.username)
})
}
handleCommentsChange(event){
this.setState({
comments: event.target.value
},
() =>{
console.log(this.state.comments)
})
}
handleTopicChange(event){
this.setState({
topic: event.target.value
},
() =>{
console.log(this.state.topic)
})
}
handleFormSubmit(event){
event.preventDefault();
alert(`${this.state.username} ${this.state.comments} ${this.state.topic}`);
}
render() {
return (
<form onSubmit={this.handleFormSubmit}>
<div>
<label>Username</label>
<input type='text' value={this.state.username} onChange={this.handleUsernameChange}/>
</div>
<div>
<textarea value={this.state.comments} onChange={this.handleCommentsChange}></textarea>
</div>
<div>
<select value={this.state.topic} onChange={this.handleTopicChange}>
<option value="react">React</option>
<option value="angular">Angular</option>
<option value="vue">Vue</option>
</select>
</div>
<button>Submit</button>
</form>
)
}
}
Hi all i made some changes and got something working, added extra state attribute called dataSubmitted set it to false then only after i submit the data is child (which i called AcceptFormData) allowed to render and i pass the state attributes as props. I do not know if this is a good approach or not but it works and no console errors.
class Form extends Component {
constructor(props) {
super(props)
this.state = {
username: '',
comments: '',
topic: 'react',
dataSubmitted: false
}
this.handleUsernameChange = this.handleUsernameChange.bind(this);
this.handleCommentsChange = this.handleCommentsChange.bind(this);
this.handleTopicChange = this.handleTopicChange.bind(this);
this.handleFormSubmit = this.handleFormSubmit.bind(this);
}
handleUsernameChange(event){
this.setState({
username: event.target.value
},
() =>{
console.log(this.state.username)
})
}
handleCommentsChange(event){
this.setState({
comments: event.target.value
},
() =>{
console.log(this.state.comments)
})
}
handleTopicChange(event){
this.setState({
topic: event.target.value
},
() =>{
console.log(this.state.topic)
})
}
handleFormSubmit(event){
event.preventDefault();
this.setState({
dataSubmitted: true
})
}
render() {
if(this.state.dataSubmitted === false){
return (
<form onSubmit={this.handleFormSubmit}>
<div>
<label>Username</label>
<input type='text' value={this.state.username} onChange={this.handleUsernameChange}/>
</div>
<div>
<textarea value={this.state.comments} onChange={this.handleCommentsChange}></textarea>
</div>
<div>
<select value={this.state.topic} onChange={this.handleTopicChange}>
<option value="react">React</option>
<option value="angular">Angular</option>
<option value="vue">Vue</option>
</select>
</div>
<button>Submit</button>
</form>
)
}else{
return (
<AcceptFormData username={this.state.username} comments={this.state.comments} topic={this.state.topic}/>
)
}
}
}
export default Form
Related
This post is very close to what I'm trying to do, but doesn't cover dynamic fields: Updating an object with setState in React
What I mean by dynamic fields: (Working Code)
import React, { Component } from 'react';
class ComponentName extends Component {
constructor(props) {
super(props);
this.state = {
name: "",
age: "",
email: "",
manager: ""
}
}
handleValueChange = (ev) => {
this.setState({[ev.target.name]: ev.target.value}); // <----- dynamic field
}
handleSubmit = (ev) => {
ev.preventDefault();
console.log("Form Values =", this.state);
}
render() {
<form onSubmit={ev => this.handleSubmit(ev)}>
<div>
<label>Name:</label>
<input type="text" name="name" onChange={ev => this.handleValueChange(ev)} value={this.state.name} />
</div>
<div>
<label>Age:</label>
<input type="text" name="age" onChange={ev => this.handleValueChange(ev)} value={this.state.age} />
</div>
<div>
<label>Age:</label>
<input type="email" name="email" onChange={ev => this.handleValueChange(ev)} value={this.state.email} />
</div>
<div>
<label>Age:</label>
<input type="text" name="manager" onChange={ev => this.handleValueChange(ev)} value={this.state.manager} />
</div>
<button type="submit">Submit</button>
</form>
}
}
export default BadgeContractorRequest;
^Notice how I use this.setState({[ev.target.name]: ev.target.value}); to avoid hardcoding every field.
What I want to do... is update multiple form objects within the same state: (Broken Code)
import React, { Component } from 'react';
// Components
import FormOne from './formOne.js';
import FormTwo from './formTwo.js';
import FormThree from './formThree.js';
class ComponentName extends Component {
constructor(props) {
super(props);
this.state = {
formRequestTypeValue: "",
formOne: {
name: "",
age: "",
requestType: "",
manager: ""
},
formTwo: {
managerName: "",
email: "",
position: ""
},
formThree: {
product: "",
details: ""
}
}
}
formRequestTypeOnChangeHandler = (ev) => {
this.setState({...this.state, formRequestTypeValue: ev.target.value});
}
handleValueChange = (ev) => {
// This is where I'm Lost...
this.state.formRequestTypeValue === "formOne" && this.setState({...this.state, formOne.[ev.target.name]: ev.target.value});
this.state.formRequestTypeValue === "formTwo" && this.setState({...this.state, formTwo.[ev.target.name]: ev.target.value});
this.state.formRequestTypeValue === "formThree" && this.setState({...this.state, formThree.[ev.target.name]: ev.target.value});
}
handleSubmit = (ev) => {
ev.preventDefault();
console.log("Form Values =", this.state);
}
render() {
<form onSubmit={ev => this.handleSubmit(ev)}>
<div>
<select onChange={ev => this.formRequestTypeOnChangeHandler(ev)} value={this.state.formRequestTypeValue}>
<option value="">Please Select a Form</option>
<option value="formOne">Form One</option>
<option value="formTwo">Form Two</option>
<option value="formThree">Form Three</option>
</select>
</div>
{
this.state.formRequestTypeValue === "formOne" &&
<div>
<FormOne handleValueChange={this.handleValueChange} handleSubmit={this.handleSubmit} formValues={this.state.formOne} />
</div>
}
{
this.state.formRequestTypeValue === "formTwo" &&
<div>
<FormTwo handleValueChange={this.handleValueChange} handleSubmit={this.handleSubmit} formValues={this.state.formTwo} />
</div>
}
{
this.state.formRequestTypeValue === "formThree" &&
<div>
<FormThree handleValueChange={this.handleValueChange} handleSubmit={this.handleSubmit} formValues={this.state.formThree} />
</div>
}
</form>
}
}
export default ComponentName;
The reason I'm not separating the <form> out into their own components, is because I want to maintain state of field values if the user selects a different form from the dropdown menu, and then goes back to their previous selection.
Here is where I'm stuck:
handleValueChange = (ev) => {
// This is where I'm Lost...
this.state.formRequestTypeValue === "formOne" && this.setState({...this.state, formOne.[ev.target.name]: ev.target.value});
this.state.formRequestTypeValue === "formTwo" && this.setState({...this.state, formTwo.[ev.target.name]: ev.target.value});
this.state.formRequestTypeValue === "formThree" && this.setState({...this.state, formThree.[ev.target.name]: ev.target.value});
}
Anyone have any idea what the syntax magic should be for handleValueChange?
Edit Update:
Also tried the following, but with no luck:
First:
You don't need to have . after formThree for example. Like below:
formThree[ev.target.name]
Then you need to have your past formOne fields to prevent them become empty. For that you need a code like below:
this.state.formRequestTypeValue === "formOne" &&
this.setState((prevState) => ({
...prevState,
formOne: {
...prevState.formOne,
[ev.target.name]: ev.target.value,
},
}));
prevState is your last this.state that has not changes yet.
with formOne: {...prevState.formOne,[ev.target.name]: ev.target.value}, you are carrying old formOne fields and also overriding [ev.target.name] field.
You need to repeat this for other form, too.
I've posted this question earlier, but probably not quite clearly formulated it. I have a chat. When I add message it goes to database but not updated on page. So I need it to be updated on page after adding a msg.
Parent component Forms
export default class Forms extends Component {
constructor() {
super()
this.state = {
messages: [],
}
this.sendMessage = this.sendMessage.bind(this);
}
componentDidMount(){
client1.getEntries({limit:300, order: 'sys.createdAt',
content_type:'nameTest'}).then(response => {
this.setState({messages: response.items});
}).catch(e => {
console.log(e);
});
}
sendMessage(data) {
client2.getSpace(client2.space)
.then((space) => space.getEnvironment('master'))
.then((environment) => environment.createEntry('nameTest', {
fields: {
chatName: {
'en-US': data.get('chatName')
... some data
}
}))
.then((entry) => entry.publish())
.catch(console.error)
}
render() {
return (
<div className="chat">
<div className="container-xl">
<MessageList messages={this.state.messages}/>
<SendMsg onSendMessage={this.sendMessage}/>
</div>
</div>
);
}
}
the child component SengMsg
export default class SendMsg extends Component {
constructor() {
super()
this.state = {
message:'',
userEmail:'ddd#gmail.com',
chatName:'ggg'
}
this.sendMessage = this.sendMessage.bind(this)
this.handleChange = this.handleChange.bind(this)
}
handleChange(e) {
this.setState({
message: e.target.value,
})
}
sendMessage(e) {
e.preventDefault();
const { onSendMessage } = this.props;
const form = e.target;
const data = new FormData(form);
// if send message handler was passed, invoke with form data
onSendMessage && onSendMessage(data);
this.setState({
message: ''
})
}
render() {
return (
<div className="send-message">
<Form className="send-msg" onSubmit={this.sendMessage}>
<FormGroup>
<Input type="hidden" name="userEmail" value={this.state.userEmail}/>
</FormGroup>
<FormGroup>
<Input type="hidden" name="chatName" value={this.state.chatName}/>
</FormGroup>
<FormGroup>
<Input
type="text"
name="text"
onChange={this.handleChange}
value={this.state.message}
placeholder="Write your message here"
required />
</FormGroup>
<FormGroup>
<Input type="hidden" name="dateCreated" value={moment().format()} onChange={this.handleChange}/>
</FormGroup>
</Form>
</div>
);
}
}
I used the same function I got on a react native in a react app and it didn't work, looks like I couldn't access the sate although I defined it in the constructor, the goal is to push data to firebase, I tried with random strings and it definitely works, it's just when using the form that it crashes.
As you can see I'm using text components to take a look a the state on the HTML page :
import React, { Component } from 'react';
import fire from './config/Fire';
class Home extends Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
isOpen: false,
title: '',
description: '',
loading: true
};
}
handleChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
saveData(e) {
e.preventDefault();
let title = this.state.title;
let description = this.state.description;
const { currentUser } = fire.auth();
fire
.database()
.ref(`/master/setup/`)
.push({ title, description })
.then(() => {
this.setState({ loading: false }).catch(error => {
console.log(error);
});
});
}
render() {
return (
<div>
<Container>
<Row>
<Col sm="2" lg="3" />
<Col sm="8" lg="6">
<h1>General Setup</h1>
<form>
<div class="form-group">
<label for="exampleInputEmail1">Title</label>
<input
value={this.state.title}
onChange={this.handleChange}
name="title"
class="form-control"
id="title"
placeholder="Enter event title"
/>
</div>
<div class="form-group">
<label for="exampleInputEmail1">Description</label>
<input
value={this.state.description}
onChange={this.handleChange}
name="description"
class="form-control"
id="description"
placeholder="Enter event description"
/>
</div>
<button onClick onClick={this.saveData} class="btn btn-primary">
Submit
</button>
</form>
<p>{this.state.title}</p>
<p>{this.state.description}</p>
<p>{this.state.loading.toString()}</p>
</Col>
<Col sm="2" lg="3" />
</Row>
</Container>
</div>
);
}
}
export default Home;
TypeError: Cannot read property 'state' of undefined
Please, someone, let me know what's going on with this code?
You can change saveData to an arrow function hence binding isn't required. This is an ES6 version, do something like below
saveData = e => {
e.preventDefault();
let title = this.state.title;
let description = this.state.description;
const { currentUser } = fire.auth();
fire.database().ref(`/master/setup/`)
.push({ title, description })
.then(() => {
this.setState({ loading: false})
.catch((error) => {
console.log(error);
})
});
}
You need to bind saveData in constructor.
this.saveData = this.saveData.bind(this);
You forgot to bind scope to saveData method.
Do it in constructor same as you bind it to handleChange method.
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.saveData = this.saveData.bind(this);
this.state = {
isOpen: false,
title: '',
description: '',
loading: true,
};
or
change saveData definition to one that uses arrow function syntax from ES6
saveData = (e) => {...function body as you already have it}
and parent scope will be bind for you by default
What is a more compact and more easy way to write a handleChange function for multiple input than the following:
import React from "react";
function initialState() {
return ({
customer: {
name: '',
primaryContactPerson: {
name: '',
email: ''
}
}
})
}
export default class AddCustomerForm extends React.Component {
constructor(props) {
super(props);
this.state = initialState();
this.handleChange = this.handleChange.bind(this);
}
handleChange(event, field) {
console.log(event.target.name);
switch(event.target.name) {
case 'customer.name':
console.log(1);
this.setState({
this: {
customer: {
name: event.target.value
}
}
})
break;
case 'customer.primaryContactPerson.name':
console.log(2);
this.setState({
this: {
customer: {
primaryContactPerson: {
name: event.target.value
}
}
}
})
break;
case 'customer.primaryContactPerson.email':
this.setState({
this: {
customer: {
primaryContactPerson: {
email: event.target.value
}
}
}
})
break;
default:
break;
}
this.setState({[event.target.name]: event.target.value});
}
render() {
return (
<div className='section section--full-width'>
<h1>Add Customer</h1>
<form onSubmit={this.handleSubmit}>
<div className='form__row'>
<label>
Customer name
</label>
<input type="text" name="customer.name" value={this.state.customer.Name} onChange={this.handleChange} />
</div>
<div className='form__row'>
<label>
Contact person
</label>
<input type="text" name="customer.primaryContactPerson.name" value={this.state.customer.primaryContactPerson.name} onChange={this.handleChange} />
</div>
<div className='form__row'>
<label>
Contact details
</label>
<input type="text" name="customer.primaryContactPerson.email" value={this.state.customer.primaryContactPerson.email} onChange={this.handleChange} />
</div>
<div>
<input type="submit" value="Add" />
</div>
</form>
</div>
);
}
}
I have tried the following method that does not work for nested objects:
handleChange(event, field) {
this.SetState({event.targetname: event.target.value})
}
One dirty hack is using lodash _.set Link. This is not the recommended method only a workaround.
you can use
const tmp = this.state;
_.set(tmp, event.target.name, event.target.value);
this.setState({ customer: tmp.customer });
if you want to avoid direct state mutation you can also use
const tmp = _.cloneDeep(this.state)
this process will be slower.
child component
import React, { Component } from 'react'
export default class Login extends Component {
constructor (props) {
super(props);
this.state = {Id: '',name: '',gender: ''};
this.show = this.show.bind(this);
}
show (event) {
if (this.state.Id === "123456" && this.state.name !== '' && this.state.gender !== '') {
this.props.show();
alert('you are login');
console.log('A ID was submitted: ' + this.state.Id);
console.log('A Name was submitted: ' + this.state.name);
console.log('A Gender was submitted: ' + this.state.gender);
} else {
alert('Please enter your valid id,Your Name & Gender');
}
event.preventDefault();
}
render () {
return (
<div className="login">
<form onSubmit={ this.show.bind(this) }>
<div>
<label>Your ID:</label>
<input type="text" onChange={ event => this.setState({ Id: event.target.value }) } placeholder="Enter your ID" />
</div>
<br />
<div>
<label>Your Name:</label>
<input type="text" onChange={ event => this.setState({ name: event.target.value }) } placeholder="Enter your Name" />
</div>
<br />
<div>
<label>Your Gender:</label>
<label>Female:</label>
<input type="radio" name="gender" value="Female" onChange=
{ event => this.setState({ gender: event.target.value }) } />
<label>Male:</label>
<input type="radio" name="gender" value="Female" onChange={ event => this.setState({ gender: event.target.value }) } />
</div>
<input type="submit" value="Submit" onClick={ this.props.comingvalue } />
</form>
</div>
)
}
}
parent component
class App extends Component {
constructor (props) {
super(props);
this.state = { Id: '', name: '', gender: '' };
}
getvalue () {
console.log('getting values as props');
this.setState({ Id: this.state.Id });
this.setState({ name: this.state.name });
this.setState({ gender: this.state.gender });
}
render () {
return (
<div className="App">
<Login comingvalue={ this.getvalue } />
<button type="button" className="btn btn-primary" onClick=
{ this.handleLogin }>Sign In</button>
</div>
);
}
}
export default App;
now here is the my question i want that when i enter value in my child component i get those values in parent compnent how i can get this please help..'i thing you peeple should know that i cut alot of code from above code there is possibilty of any other error but i want to know only one thing which i mention above i want child coponents value in parent component.. please suggest me right solution..thanks
Just a pointer for future posts: the less code the better and please, for the love of God, make sure the formatting is correct.
A standard pattern in React for passing information back up the tree is to pass children a callback as a prop.
parent
class Parent extends React.Component {
onChildCallback = (data) => {
alert(data)
}
render() {
return (
<div>
...
<Child onAction={this.onChildCallback}/>
</div>
)
}
}
child
class Child extends React.Component {
render() {
return (
<button onClick={() => this.props.onAction('hello from the child')}>
Click Me!
</button>
)
}
}
this is, of course, simplified, but you can extend it however you like. Some things to watch out for:
make sure you're either binding the callback in the parent or using arrow functions (in this case, I'm using a ES7 class property)
if you need data from a child of a child, you need to chain these... you can get away with using context, but ... don't. Just don't.