Reactjs pass variables from one component to another - reactjs

I have a component like this:
class LoginPage extends React.Component{
constructor(props) {
super(props);
this.state = {submit:''};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e) {
alert('username and password were submitted: ' + this.props.userName + ' and ' + this.props.password);
e.prevenDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<GetUsername/>
<GetPassword/>
<button type="submit">Login</button>
</form>
)
}
}
The GetUsername and GetPassword components get username and password from user input. My question is that is there anyway I can pass the username and password from these 2 components to the handleSubmit method of the LoginPage component above, so the alert pops up can show correctly?

To achieve what you are looking for, you would need the login form to have its own state which would include the username and password. It would have instance methods that would update those fields. It would pass those functions down to the relevant child components and whenever they change they would call this.props.onChange with the appropriate value. The parent component would then update its appropriate state. Here is an example.
class LoginPage extends React.Component{
constructor(props) {
super(props);
this.state = {
submit:'',
password: '',
userName: ''
};
this.handleSubmit = this.handleSubmit.bind(this);
this.onUserNameChange = this.onUserNameChange.bind(this);
this.onPasswordChange = this.onPasswordChange.bind(this);
}
onUserNameChange(userName) {
this.setState({
userName: userName
})
}
onPasswordChange(password) {
this.setState({
password: password
})
}
handleSubmit(e) {
alert('username and password were submitted: ' + this.state.userName + ' and ' + this.state.password);
e.prevenDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<GetUsername onChange={onUserNameChange}/>
<GetPassword onChange={onPasswordChange}/>
<button type="submit">Login</button>
</form>
)
}
}
this may require some tweaking and as stated before you would need to make sure that you are ingesting the this.props.onChange appropriately in your GetUsername and GetPassword components so they call that function and pass the appropriate argument in.

You need to make username and password part of the state of your LoginPage component, and update the state whenever the input changes. You do this by having an inputChanged function defined on LoginPage which is passed as a prop to your GetUsername and GetPassword components.
class LoginPage extends React.Component {
constructor() {
super();
this.state = {
username: '',
password: ''
};
}
handleSubmit(e) {
e.prevenDefault();
alert('username and password were submitted: ' + this.state.userName + ' and ' + this.state.password);
}
handleInputChange(name, value) {
this.setState({
[name]: value
});
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<GetUsername onChange={newValue => this.handleInputChange('username', newValue)} />
<GetPassword onChange={newValue => this.handleInputChange('password', newValue)} />
<button type="submit">Login</button>
</form>
)
}
Then e.g.
const GetUsername = ({ onChange }) => (
<input type="text" name="username" onChange={(e) => onChange(e.target.value)} />
);

Related

calling function in React SetState gives error that userName is unlabelled why?

import React,{Component} from 'react'
class Formhandler extends Component {
constructor(props) {
super(props)
this.state = {
userName:""
}
}
changer=(event)=>{
this.setState(()=>{
userName : event.target.value
})
}
render()
{
return(
<div>
<label>UserName</label>
<input type="text" value={this.state.userName} onChange={this.changer}/>
</div>
)
}
}
export default Formhandler
You are getting the error because of invalid syntax.
Update changer function
changer = (event) => {
this.setState({ userName: event.target.value });
};
You need to return an object inside the setState function but you are not that's the source of issue(syntax error).
use a function inside setState when your new state value would depend on your previous state value, where the function passed inside the setState will receive previous state as argument
changer = (e) => {
this.setState((prevState) => ({
userName : e.target.value
})
);
}
pass an object to update the state, use this when it doesn't depend on your previous state value.
changer = (e) => {
this.setState({ userName: e.target.value });
};
import React from "react";
class Formhandler extends React.Component {
constructor(props) {
super(props);
this.state = {
userName: "",
};
}
changer(event) {
this.setState(() => ({
userName: event.target.value,
}));
}
render() {
return (
<div>
<label>UserName</label>
<input
type="text"
value={this.state.userName}
onChange={this.changer.bind(this)}
/>
</div>
);
}
}
export default Formhandler;
It will work, compare your version and this

React : How can I edit the form?

I am using localStorage to store the form details. When the component is mounted I am getting the data in the console. How can I show the data in the form and edit it? I've set the edited state but I am not getting how can I achieve this so that the values will be prefilled?
Here's the code :
class MileStoneForm extends Component {
constructor(props){
super(props)
this.state={
deliverable_name:"",
due_date:"",
deliverable_notes:"",
milestone_based_payment:false,
deliverable_name_error:"",
due_date_error:"",
deliverable_notes_error:"",
percent_rate:0,
percent_rate_error:"",
due_date_select:false,
edit:false,
milestonedata:null;
}
}
componentDidMount(){
let milestonedata=JSON.parse(localStorage.getItem('mileStoneData'))
console.log(milestonedata)
if(this.state.edit===true){
this.setState({
milestonedata:milestonedata
},()=>{this.setEditMileStoneData()})
}
}
setEditMileStoneData=()=>{
const {milestonedata}=this.state
let data={
deliverable_name:milestonedata.milestoneName,
deliverable_notes:milestonedata.description,
due_date:milestonedata.dueDate,
milestone_based_payment:milestonedata.isMilestoneBasedPayment,
percent_rate:milestonedata.percentageRate
}
this.setState({...data})
}
handleSubmit=()=>{
const {deliverable_name,deliverable_name_error,deliverable_notes,deliverable_notes_error,
due_date,due_date_error,milestone_based_payment,percent_rate}=this.state
let pass=true
if(pass){
let data={
description: deliverable_notes,
dueDate: due_date,
isDeleted: false,
isMilestoneBasedPayment: milestone_based_payment,
milestoneName: deliverable_name,
percentageRate: percent_rate,
}
console.log(data)
this.props.handleData(data)
localStorage.setItem('mileStoneData',JSON.stringify(data))
this.setState({
deliverable_name:'',
deliverable_name_error:'',
deliverable_notes:'',
deliverable_notes_error:'',
milestone_based_payment:false,
percent_rate:'',
due_date:'',
due_date_error:''
})
}
}
export default MileStoneForm
You should take a look at the "controlled components" section from React docs.
The thought behind it is:
You create a state object for your form.
You add the state for each form field as its value
You add a function to onChange that updates the satate.
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
Controlled component is the way to go here. I usually like to do it this way.
handleChange = (e) => {
// This way when we a change is made within the field this function is called and the name from the input will be changed with the new value provided
this.setState({ [e.target.name] : e.target.value })
}
render(){
<TextInput value = { this.state.NAME_THIS_CORRELATING_TO_THE_KEY_IN_STATE } name = "NAME_THIS_CORRELATING_TO_THE_KEY_IN_STATE" onChange = { this.handleChange }
}
We then do this for all of our components

props not being passed properly - reactjs

I have two components, one of which is used for filling out a form, the other is for displaying the input once it is submitted. However, it currently only displays the input until the form is submitted, and then it goes away. What is happening to the state of the parent component when the form is submitted?
class Form extends Component {
constructor(props) {
super(props);
this.state = {
equation: null
};
}
render() {
return (
<div>
<form onSubmit={this.mySubmitHandler}>
<input
type="text"
name="equation"
onChange={this.handleInputChange}
/>
</form>
<Parser value={this.state.equation}/>
</div>
);
}
handleInputChange = event => {
event.preventDefault();
this.setState({
[event.target.name]: event.target.value
});
};
mySubmitHandler = event => {
event.preventDefault();
this.setState({ equation: event.target.value });
alert("You are submitting " + this.state.equation);
console.log(this.state.equation);
};
}
class Parser extends Component {
render() {
return <div>{this.props.value}</div>;
}
The problem here is the event.target.value from the form submit event.
Since the event is coming from form submit, the target element is form and there is no value in the target element.
Update the component like below will solve your problem.
import React, {Component } from 'react';
export default class Hello extends Component {
constructor(props) {
super(props);
this.state = {
equation: null
};
}
render() {
return (
<div>
<form onSubmit={this.mySubmitHandler}>
<input
type="text"
name="equation"
onChange={this.handleInputChange}
/>
<button type="submit">Submit</button>
</form>
<Parser value={this.state.equation}/>
</div>
);
}
handleInputChange = event => {
event.preventDefault();
this.setState({
[event.target.name]: event.target.value
});
};
mySubmitHandler = event => {
event.preventDefault();
alert("You are submitting " + this.state.equation);
console.log(this.state.equation);
};
}
class Parser extends Component {
render() {
return <div>{this.props.value}</div>;
}
}
Check the stackblitz solution.Stackblitz
In mySubmitHandler, event.target.value is undefined, which is why the Parser text is disappearing. If you need to use equation in the submit handler, just use this.state.equation because it is has already been set via handleInputChange
mySubmitHandler = event => {
event.preventDefault();
// event.target.value is undefined
// this.state.equation has already been set via this.handleInputChange
this.setState({ equation: event.target.value });
alert("You are submitting " + this.state.equation);
console.log(this.state.equation);
};
You should not be using this.setState({equation: event.target.value}); in mySubmitHandler.
The event.target for submit is the form itself and it has no value.
Therefore it sets equation to undefined.
The reason you see the correct state when you console.log() it is because setState is async and the state in that function call still has the old value with it.
Remove it and see if it works.
You need to maintain two states equation and inputequation.
Now when you change input setstate inputequation. When you submit setstate equation to inputequation.
And one more thing
<input value={this.state.inputequation}/>
input should be controlled via your state.
Here, I modified the handleInputChange and mySubmitHandler you use this
handleInputChange = event => {
event.preventDefault();
this.setState({
equation: event.target.value
});
};
mySubmitHandler = event => {
event.preventDefault();
alert("You are submitting " + this.state.equation);
console.log(this.state.equation);
};
You should check this stackblitz solution
I have used your code.
import React, { Component } from 'react';
class Form extends Component {
constructor(props) {
super(props);
this.state = {
equation: null
};
}
render() {
return (
<div>
<form onSubmit={this.mySubmitHandler}>
<input
type="text"
name="equation"
onChange={this.handleInputChange}
/>
</form>
<Parser value={this.state.equation}/>
</div>
);
}
handleInputChange = event => {
this.setState({
[event.target.name]: event.target.value
});
};
mySubmitHandler = event => {
event.preventDefault();
alert("You are submitting " + this.state.equation);
};
}
class Parser extends Component {
render() {
return <div>{this.props.value}</div>;
}
}
Simple fix. You are accessing the incorrect property of the form.
this.setState({ equation: event.target.value });
This needs to be the name of the form element which is equation:
this.setState({ equation: event.target.equation.value });
Your updated handler:
mySubmitHandler = event => {
event.preventDefault();
this.setState({ equation: event.target.equation.value });
};
JSFiddle

I have two set of input fields for two different forms. How can I use them dynamically for any form?

I am new to react. I am trying to build dynamic form component of a set of input fields using react which can be reused in any single form as a set of inputs. How can I access those input data dynamically?
For example, the form for the set of reusable inputs:
export default class dynamicForm extends Component {
handleSubmit = event => {
// get dynamic input data. But how?
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<CustomInputs1/>
<CustomInputs2/>
<input type="submit" value="confirm"/>
</form>
)}
Input set no. 1 as custom input:
export default class CustomInputs1 extends Component {
constructor(props) {
super(props);
this.state = {
input1: "",
input2: ""
};
this.inputInput1Ref = React.createRef();
this.inputInput2Ref = React.createRef();
}
handleInputChange = event => {
event.preventDefault();
this.setState({
[event.target.name]: event.target.value
});
};
render() {
const { input1 } = this.state;
const { input2 } = this.state;
return (
<div>
<input type="text" name="input1" value={input1} onChange={this.handleInputChange}/>
<input type="text" name="input2" value={input2} onChange={this.handleInputChange}/>
</div>
)
}
}
Input set no. 2 as custom input:
export default class CustomInputs2 extends Component {
constructor(props) {
super(props);
this.state = {
input3: "",
input4: ""
};
this.inputInput3Ref = React.createRef();
this.inputInput4Ref = React.createRef();
}
handleInputChange = event => {
event.preventDefault();
this.setState({
[event.target.name]: event.target.value
});
};
render() {
const { input3 } = this.state;
const { input4 } = this.state;
return (
<div>
<input type="text" name="input3" value={input3} onChange={this.handleInputChange}/>
<input type="text" name="input4" value={input4} onChange={this.handleInputChange}/>
</div>
)
}
}
There can be more reusable input groups like this.I just added two group as example.
A few things: You are not using the refs you are creating in the CustomInputs, so those lines can go. Also, you can destructure the state in one line, like so: const {input1, input2} = this.state.
Now for your problem: You can either create refs to the CustomInputs in your dynamicForm and simply access the state of your custom inputs when the form is submitted or pass a function as props to each CustomInputs which writes the input to the state of dynamicForm. First option is probably easier ;)

Meteor login error cause unmounted component

I know this has been asked before, but I'm not understanding what's causing the problem. In this case when a user submits the login request handleSubmit is called and the page runs Meteor.loginWithPassword. The problem occours when there is a server error, ie. the users password is incorrect. this.setState({ loginError: true }) returns a console error:
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the LoginPage component.
I realise it's something to do with the component unmounting when the form is submitted but I don't understand how I suppose to fix it.
export default class LoginPage extends React.Component {
constructor(props) {
super(props);
this.state = {
email: '',
errors: {},
password: '',
loginError: false
};
this.handleInputChange = this.handleInputChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
handleSubmit(event) {
event.preventDefault();
Meteor.loginWithPassword(email, password, (error, result) => {
if (error) {
this.setState({ loginError: true });
} else {
console.log("success");
}
});
}
render() {
return (
<div>
<form noValidate>
{this.state.loginError
?
<div className="alert alert-danger text-center" role="alert">
Incorrect username or password.
</div>
: null
}
<SingleInput
name={'email'}
inputType={'email'}
controlFunc={this.handleInputChange}
content={this.state.email}
placeholder={'Email'}
bsSize={null}
error={this.state.errors && this.state.errors.email}
/>
<SingleInput
name={'password'}
inputType={'password'}
controlFunc={this.handleInputChange}
content={this.state.password}
placeholder={'Password'}
bsSize={null}
error={this.state.errors && this.state.errors.password}
/>
<button className="btn btn-success btn-block" onClick={this.handleSubmit}>Login</button>
</form>
</div>
);
}
}
The error is likely in the parent component that calls LoginPage
<App>
<HOCWrapper component={LoginPage} {...this.props} />
</App>
When you're calling this.setState(), it's not updating the Higher Order Component (AsyncWrapper), so it doesn't know to re-render to component.
So, it's passing the state to the HOC, not to LoginPage
const HOCWrapper = LoginPage => {
constructor() {
super()
this.state = {
loginError: true
}
}
So you need to update the HOC's state, and pass it back to the props with something like:
const HOCUpdater = Login => {
constructor() {
super()
this.state = {
loginError: []
}
}
return class HOCUpdater extends Component {
componentWillReceiveProps(nextProps){
this.setState(nextProps);
}
render(){
return createElement(LoginPage,{...this.state});
}
}
}

Resources