pass formik.values to another js file - reactjs

I learn react and I am trying to create a stepper to manage my form correct,I'm looking for the best fit way to pass input value from one step to another
The full code is bellow
here the input value from step 1 :
<MDBInput
label="exerciseManager"
name="exerciseManager"
type="text"
value={formik.values.exerciseManager}
onChange={formik.handleChange}
outline
size="lg"
/>
I want to pass formik.values.exerciseManager to step 2 so i can use it there
step 1 and step 2 in different js file
how should I do that correct?
this is step 1 :
I want it will show here :
full step 1 code
//now check that the value get correct to the database
const Certifications = (props) => {
//state //Setstate
const [fieldApproveOptions, setFieldApproveOptions] = useState([])
//useEffect Hooks
useEffect(() => {
axios.get('/api/certifications/fieldApproveOptions?userId=1234567&rank=Colonel&force=Moran')
.then(response => {
console.log(response.data)
setFieldApproveOptions(response.data.fieldApproveOptions)
}
).catch(err => console.log(err))
}, [])
const formik = useFormik({
initialValues: {
exerciseName: '',
//exerciseBy: '', autofill current user from session
exerciseOOB: '',
exercisePOD: '',
exerciseType: '', // options should be pull from db
exerciseLive: '',
fieldApprove: '', // options should be pull from db
fileApprove: '', // options should be pull from db
artilleryApprove: '', // options should be pull from db
exerciseManager: '',
trainerOfficerApprove: '', // options should be pull from db
cerRes: '',
fieldApproveOptions: []
},
onSubmit: values => {
axios.post('/api/certifications', values)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
},
})
return (
<Card>
<CardContent>
<div className="Pedding">
<MDBContainer fluid >
<MDBRow center >
<MDBCol md="4" >
<div className="MDBColor">
<form onSubmit={formik.handleSubmit} autoComplete="off">
<p className="h1 text-center" style={{ paddingTop: "10px", fontWeight: "bold" }}>Certifications</p>
<div className="Certifications">
<MDBInput
label="Exercise name"
name="exerciseName"
type="text"
onChange={formik.handleChange}
value={formik.values.exerciseName}
outline
size="lg"
/>
<MDBInput
label="Exercise type"
name="exerciseType"
list="types"
onChange={formik.handleChange}
value={formik.values.exerciseType}
outline
size="lg"
/>
<datalist id="types" >
<option data-value="1" value="Open Terrain">Open Terrain</option>
<option value="Urban warfare" >Urban warfare</option>
<option value="Armoured fighting vehicle" >Armoured fighting vehicle</option>
<option value="unplanned" >unplanned</option>
<option value="live military exercise" >live military exercise</option>
</datalist>
<MDBInput
label="Order of battle"
name="exerciseOOB"
type="number"
min="20"
onChange={formik.handleChange}
value={formik.values.exerciseOOB}
outline
size="lg"
/>
{/*FieldApprove button */}
<MDBInput
label="fieldApprove"
name="fieldApprove"
list="fieldApproves"
onChange={formik.handleChange}
value={formik.values.fieldApprove}
outline
size="lg"
/>
<datalist id="fieldApproves" defaultValue>
{fieldApproveOptions.map((option, i) =>
<option key={i++} value={option.id}>
{option.rank + " " + option.firstName + " " + option.lastName}
</option>)}
</datalist>
<MDBInput
label="fileApprove"
name="fileApprove"
type="text"
value={formik.values.fileApprove}
onChange={formik.handleChange}
outline
size="lg"
/>
<MDBInput
label="artilleryApprove"
name="artilleryApprove"
type="text"
value={formik.values.artilleryApprove}
onChange={formik.handleChange}
outline
size="lg"
/>
<MDBInput
label="exerciseManager"
name="exerciseManager"
type="text"
value={formik.values.exerciseManager}
onChange={formik.handleChange}
outline
size="lg"
/>
<MDBInput
label="trainerOfficerApprove"
name="trainerOfficerApprove"
type="text"
value={formik.values.trainerOfficerApprove}
onChange={formik.handleChange}
outline
size="lg"
/>
<div style={{ fontSize: "large", fontWeight: "bold" }} className="custom-control custom-checkbox">
<input type="checkbox"
onChange={formik.handleChange}
value={formik.values.exerciseLive}
className="custom-control-input"
name="exerciseLive"
id="live"
value="on"
/>
<label className="custom-control-label" htmlFor="live"> live exercise</label>
</div>
{/*pod section*/}
<span style={{ fontSize: "large", fontWeight: "bold", float: "left" }} >part of the day:</span>
<div className="forms" style={{ fontWeight: "bold" }} onChange={formik.handleChange} value={formik.values.exercisePOD} >
{/*night button*/}
<label htmlFor="rdo1">
<input type="radio" id="rdo1" name="exercisePOD" value="night" />
<span className="rdo"></span>
<span>night</span>
</label>
{/*day button*/}
<label htmlFor="rdo2">
<input type="radio" id="rdo2" name="exercisePOD" value="day" />
<span className="rdo"></span>
<span>day</span>
</label>
</div>
<div className="text-center">
<MDBBtn type="submit" color="yellow">Send</MDBBtn>
</div>
</div >
</form >
</div>
</MDBCol>
</MDBRow>
</MDBContainer >
</div>
</CardContent>
</Card>
);
}
export default Certifications;
here Soform.js full code
const SoForm = () => {
return (
<p >
{formik.values.exerciseManager}
</p>
)
}
export default SoForm;
Here formikStepper code
export default function Home() {
return (
<Card>
<CardContent>
<FormikStepper
initialValues={{
}}
onSubmit={async (values) => {
await sleep(3000);
console.log('values', values);
}}
>
<FormikStep label="Certifications">
<Box paddingBottom={2}>
<Certifications ></Certifications>
</Box>
</FormikStep>
<FormikStep
label="loginPageF"
>
<Box paddingBottom={2}>
<SoForm ></SoForm>

It's really hard to read and understands your code. Try to provide minimal reproducible statements. I can understand you problem, but can't work with your code. So I have created my won example. Here, codesandbox.io : https://codesandbox.io/s/elegant-shtern-362ki?file=/src/App.js
Let me explain it for you.
index.js -> is entry point, that calls only one component App, as we have no route
App.js -> is the parent component, which has two child, Certification and Panel
Certification.js -> takes input
Panel -> renders data from certification
If I'm not wrong, that's what you want. Render data in a component to another component, which are siblings to one another.
App.js :
import React from "react";
import Certifications from "./Certifications";
import Panel from "./Panel";
class App extends React.Component {
state = {
value1: "",
value2: ""
};
fromCert = (name, value) => {
this.setState({ [name]: value });
};
render() {
return (
<div>
{this.state.value1}
{this.state.value2}
<Certifications fromCert={this.fromCert} />
<Panel value1={this.state.value1} value2={this.state.value2} />
</div>
);
}
}
export default App;
Panel.js:
import React from "react";
class Panel extends React.Component {
render() {
return (
<div>
Here we will render data from App, below
<div>{this.props.value1}</div>
<div>{this.props.value2}</div>
Panel ends
</div>
);
}
}
export default Panel;
Certification.js:
import React from "react";
class Certifications extends React.Component {
state = {
input1: "",
input2: ""
};
onChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
this.props.fromCert(e.target.name, e.target.value);
};
render() {
return (
<div>
<input name="value1" onChange={this.onChange} />
<input name="value2" onChange={this.onChange} />
<div>
Inside certificate
<div>{this.state.input1}</div>
<div>{this.state.input2}</div>
Certificate ends
</div>
</div>
);
}
}
export default Certifications;
First of all, Certifications have two states - value1, value2. So I have given two input's name value1, value2. So that I can set value dynamically. Why? - just imagine I have 10 or 20 states. And that you have in your code.
And onChange of value, I'm triggering a callback to App with fromCert. Try passing 1000+ parameter, it will. :wink:
When I'm triggering that callback from certification, it's getting called in App.js's fromCert. Why is that? Cause I have passed that function or method as parameter - <Certifications fromCert={this.fromCert} />. So that's the reason why this function or method acts as callback. When I get those values, I just set them to App.js's state. Then pass them as props to Panel.
So whatever changes gets applied to Certification that comes to App, which then gets passed to Panel.
Certification -> App : Child to Parent communication
App -> Panel : Parent to Child communication
Certification -> Panel : Child to Child communication / Sibling communication
Try to imagine the hierarchy.
Thanks a lot. If you have any question, feel free to ask.

Related

Selecting input element form of a react functional component

could u help a curious intern developer? ^^
I'm using the form inside a functional react component, there is 4 "fake pages" that Im controlling by buttons to hide/show the others input elements. Everytime the user changes the page by clicking in "Next Page" button Im calling a function to see if the inputs present on that page is correctly filled, If something is missing I wanna show an alert and focus (for example) to the corresponding input.
I already tried to use Jquery but got error saying that $(..) is not defined.
I read in some pages that I could use Refs to make this happen but yet in these pages says that its not indicated to use too much refs in a component, now Im wondering if there is another way to accomplish my goal.
Whats the best way to do this?
My code:
const handlePageChange = (e, pageNumber, action) => {
if(action === '+'){
if(pageNumber === '1'){
const nameArray = contactName.split('' );
if(nameArray.length < 2 || nameArray[1] == ''){
alert('Please inform your full name.');
//$("#contactName").focus();
}else{setPageNumber('2')}
}
}
}
return (
<div className="row">
<form onSubmit={handleSubmit} className="col s12">
{pageNumber==='1' &&
<div id='contact-section' className="row form-section">
<label className='active form-tittle'>Informações de Contato (1/4)</label>
<div className="input-field col s12 ">
<input id="contactName" type="text" className="validate"
value={contactName} onChange={e => setContactName(e.target.value)}
/>
<label className='active' htmlFor="contactName">Nome</label>
</div>
<div className="input-field col s12">
<input id="contactPhones" type="text" className="validate"
value={contactPhones} onChange={e => setContactPhones(e.target.value)}
/>
<label className='active' htmlFor="contactPhones">Telefone de Contato</label>
</div>
<div className="input-field col s12">
<input id="contactEmail" type="email" className="validate"
value={contactEmail} onChange={e => setContactEmail(e.target.value)}
/>
<label className='active' htmlFor="contactEmail">E-mail</label>
</div>
<div className='buttons-box'>
<a className="waves-effect waves-light yellow darken-2 btn" onClick={e=> handlePageChange(e, '1','+')}><i className="material-icons right">chevron_right</i>Next Page</a>
</div>
</div>}
In React, given the advent of JSX, you don't really need to use JQuery.
If you are planning to fully leverage the power of React, or maybe you're confused on why you don't really need JQuery, I suggest to read up on this article...
Refs provide a way to access DOM nodes or React elements created in
the render method.
Now, just taking a look at your code snippet, I suggest you take a different approach. Instead of wrapping all three of your "fake page" components in a form, think about having an entire form per "fake page" (three forms total if you have three pages). Note that you don't have to actually perform a form submit when they hit the next button, but you could just intercept that event, perform validation, accumulate the values into a React context or higher level hook, and render the next page.
With React, (just like other things) you have to learn to think in React. Make sure that you hash out different approaches when you run into problems like this, because if your code gets too spaghetti-like with a bunch of refs pointing everywhere, there is likely a simpler, more elegant approach.
If you are using React.js, I would be inclined to suggest formik or some framework to abstract away some of the complexity of dealing with form state and validation. It will make your life a whole lot easier. I personally use Ant Design's Steps with each Step containing a form. You can always shell out to scss, tailwindcss, less, or the style prop to override Ant Design's classic styles.
Here is an untested example I threw together using Ant Design just so you can understand conceptually where I am coming from.
import React, { useState } from "react";
import { Form, Button, Divider, Steps, message } from "antd";
import { PageOne, PageTwo, PageThree } from "./Pages";
const { Step } = Steps;
function StepperForm() {
const [current, setCurrent] = React.useState(0);
const [globalValues, setGlobalValues] = useState([]);
const [PageOneForm] = Form.useForm();
const [PageTwoForm] = Form.useForm();
const [PageThreeForm] = Form.useForm();
const steps = [
{
title: "Page One",
content: <PageOne formRef={PageOneForm} />,
},
{
title: "Page Two",
content: <PageTwo formRef={PageTwoForm} />,
},
{
title: "Page Three",
content: <PageThree formRef={PageThreeForm} />,
},
];
const prev = () => {
setCurrent(current - 1);
var it = globalValues;
it.pop();
setGlobalValues(it);
};
const next = () => {
switch (current) {
case 0:
PageOneForm.validateFields()
.then((values) => {
setGlobalValues([values]);
setCurrent(current + 1);
})
.catch((errorInfo) => {
message.error(
"You cannot move on until all " +
errorInfo.errorFields.length +
" errors are resolved."
);
});
break;
case 1:
PageTwoForm.validateFields()
.then((values) => {
var it = onBoardValues;
it.push(values);
setGlobalValues(it);
setCurrent(current + 1);
})
.catch((errorInfo) => {
message.error(
"You cannot move on until all " +
errorInfo.errorFields.length +
" errors are resolved."
);
});
break;
case 2:
PageThreeForm.validateFields()
.then((values) => {
var it = onBoardValues;
it.push(values);
setGlobalValues(it);
setCurrent(current + 1);
//call api.
})
.catch((errorInfo) => {
message.error(
"You cannot move on until all " +
errorInfo.errorFields.length +
" errors are resolved."
);
});
break;
}
};
return (
<>
<Steps current={current}>
{steps.map((item) => (
<Step key={item.title} title={item.title} />
))}
</Steps>
<Divider />
<div>{steps[current].content}</div>
<div>
{current > 0 && <Button onClick={() => prev()}>Previous</Button>}
{current < steps.length - 1 && (
<Button onClick={() => next()}>Next</Button>
)}
{current === steps.length - 1 && (
<Button onClick={() => next()}>Done</Button>
)}
</div>
</>
);
}
Where the PageOne component might look like
import React from "react";
import { Form, Input, Row, Col } from "antd";
export function PageOne(props) {
const [form] = Form.useForm();
return (
<div>
<Form
size={"medium"}
form={props.formRef}
name="PageOne"
label="left"
layout="vertical"
>
<Row>
<Col>
<Form.Item
validateTrigger="onBlur"
name="FirstName"
label={"First Name"}
rules={[
{
required: true,
message: "Please enter your first name.",
whitespace: true,
},
]}
>
<Input />
</Form.Item>
</Col>
<Col>
<Form.Item
validateTrigger="onBlur"
name="LastName"
label={"Last Name"}
rules={[
{
required: true,
message: "Please enter your last name.",
whitespace: true,
},
]}
>
<Input />
</Form.Item>
</Col>
</Row>
<Row>
<Col>
<Form.Item
validateTrigger="onBlur"
name="EmailAddress"
label="Primary E-mail"
rules={[
{
type: "email",
message: "Enter a valid E-mail.",
},
{
required: true,
message: "Please enter your E-mail.",
},
]}
>
<Input />
</Form.Item>
</Col>
<Col>
<Form.Item
validateTrigger="onBlur"
name="PhoneNumber"
label="Phone Number"
hasFeedback
rules={[
{
required: true,
message: "Phone number required for MFA.",
},
({ setFieldsValue }) => ({
validator(_, value) {
if (!value) {
return Promise.reject();
}
value = value.toString();
value = value.replace(/[^\d]/g, "");
setFieldsValue({ PhoneNumber: value });
if (value.length < 10) {
return Promise.reject(
"Phone number can't be less than 10 digits"
);
}
if (value.length > 10) {
return Promise.reject(
"Phone number can't be more than 10 digits"
);
}
return Promise.resolve();
},
}),
]}
>
<Input />
</Form.Item>
</Col>
</Row>
<Row>
<Col>
<Form.Item
validateTrigger="onBlur"
name="Password"
label="Password"
rules={[
{
required: true,
message: "Please enter your password.",
},
]}
hasFeedback
>
<Input.Password />
</Form.Item>
</Col>
<Col>
<Form.Item
validateTrigger="onBlur"
name="ConfirmPassword"
label="Confirm Password"
dependencies={["password"]}
hasFeedback
rules={[
{
required: true,
message: "Please confirm your password.",
},
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue("Password") === value) {
return Promise.resolve();
}
return Promise.reject(
"The two passwords that you entered do not match."
);
},
}),
]}
>
<Input.Password />
</Form.Item>
</Col>
</Row>
</Form>
</div>
);
}
As you can see, component frameworks help you out a lot (even with form validation). I personally like how ANTD has it included. If you are just learning, there is some value in re-creating the wheel for learning's sake, but after a while, it is important to learn that you don't need to every time.
P.S. Next time, include your complete component code formatted. It makes it hard for us to read and comprehend otherwise.

Why it doesn't save input data to state variable inside <Collapse> tag in React

I'm writing a code to implement a vertical Collapse by using react material UI. But it won't set the Input and TextField values into the state variables. Please help me to find what i did wrong here. Thanks in advance.
Here is the function that is used to handle input changes
handleOnChange = async (event) => {
const field = event.target.name;
if (field === "Announcement") {
if (this.isMount) {
await this.setState({
announcement: event.target.value,
isFormDirty: true,
});
}
}
if (field === "Title") {
if (this.isMount) {
await this.setState({
title: event.target.value,
isFormDirty: true,
});
}
}
};
Part of my render() function
<form>
<div>
<ListItem button onClick={this.handleClickAnnouncement}>
<ListItemAvatar>
<Avatar>
<NotificationImportant/>
</Avatar>
</ListItemAvatar>
<ListItemText primary="Announcements"/>
{this.state.ListAnnouncementOpen === true ? <ExpandMore/> : <ExpandLess/>}
</ListItem>
<Collapse in={this.state.ListAnnouncementOpen} timeout="auto" unmountOnExit>
<div className="row" className = "ml-3" >
<label htmlFor="" className="mandatory">
Title :
</label>
<Input
placeholder="Title"
value={this.state.title}
name="Title"
onChange={this.handleOnChange}
required={true}
/>
</div>
<div className="row ml-3">
<TextField
id="standard-multiline-flexible"
label="Type new Announcement here"
onChange={this.handleOnChange}
className="course-detail-section"
name="Announcement"
required={true}
value={this.state.announcement}
/>
<Avatar onClick={this.addAnnouncement}>
<AddIcon/>
</Avatar>
</div>
</Collapse>
</div>
</form>

react is editing all array objects instead of just one array object

Currently a user can enter first, lastname, and employee id. When it submits it renders an iteration of employees with the first, last, and employee id.
The problem is when you click edit, it edits the data but it will edit all of the fields for every object in the employees iteration.
How can I fix the code so that it will edit just that particular object within that iteration.
Home component
........
class Login extends Component {
constructor(props) {
super(props);
this.state = {
activeTab: '3',
firstName: '',
lastName: '',
employeeID: '',
employees: [],
Edit: false,
updatedFirst: '',
updatedLast:'',
updatedEmployeeID: ''
};
}
toggle = (tab) => {
if(this.state.activeTab !== tab){
this.setState({
activeTab: tab
})
}
}
onSubmit = (e) => {
e.preventDefault();
const {firstName, lastName, employeeID} = this.state
const ourForm ={
firstName: firstName,
lastName: lastName,
employeeID: employeeID,
// we need an id to so that it can be edited properly
}
this.setState({
employees: [...this.state.employees,ourForm]
}, () => {
console.log(this.state.employees)
})
}
onChange = (e) => {
e.preventDefault()
// e.preventDefault();
this.setState({
[e.target.name]: e.target.value,
});
}
updatedChange = (e) => {
e.preventDefault()
// e.preventDefault();
this.setState({
[e.target.name]: e.target.value,
});
}
onEdit = (e) => {
e.preventDefault();
this.setState({
Edit: !this.state.Edit
})
}
onReset = (e) => {
e.preventDefault();
this.setState({
firstName: '',
lastName: '',
employeeID: ''
})
}
render(){
return (
......
<MyForm
onSubmit={this.onSubmit}
onChange={this.onChange}
onReset={this.onReset}
firstName={this.state.firstName}
lastName={this.state.lastName}
employeeID={this.state.employeeID}
/>
</Col>
</Row>
</TabPane>
</TabContent>
</Container>
<List
employees={this.state.employees}
Edit={this.state.Edit}
onEdit ={this.onEdit}
onChange={this.onChange}
updatedEmployeeID={this.state.updatedEmployeeID}
updatedFirst={this.state.updatedFirst}
updatedLast={this.state.updatedLast}
/>
</div>
);
}
}
export default Login;
Form.js
import React, {Component} from 'react';
import { Col, Form, FormGroup, Label, Input, Button } from 'reactstrap';
const MyForm = (props) => {
return(
<Form style={{ margin: '30px 0px'}}>
<FormGroup row>
<Label for="firstName" sm={2} size="sm">First Name:</Label>
<Col sm={10}>
<Input type="text" onChange={props.onChange} value={props.firstName} name="firstName" id="exampleEmail" placeholder="Enter First Name"/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="lastName" sm={2} size="sm">Last Name:</Label>
<Col sm={10}>
<Input type="text" onChange={props.onChange} value={props.lastName} name="lastName" id="exampleEmail2" placeholder="Enter Last Name" />
</Col>
</FormGroup>
<FormGroup row>
<Label for="Employee ID" sm={2} size="sm">Employee ID:</Label>
<Col sm={5}>
<Input type="text" onChange={props.onChange} value={props.employeeID} name="employeeID" id="exampleEmail2" placeholder="Enter Employee ID" />
</Col>
</FormGroup>
<FormGroup row>
<Col sm={12}>
<div className="float-right">
<Button onClick={props.onSubmit} size="lg" style={{ margin: '0px 5px'}} color="secondary">Add</Button>
<Button onClick={props.onReset} size="lg" style={{ margin: '0px 5px'}}color="warning">Reset</Button>
</div>
</Col>
</FormGroup>
<hr></hr>
<FormGroup row>
<Col sm={4}>
<Input type="text" name="search" id="exampleEmail2" placeholder="Search" />
</Col>
<Col sm={8}>
<Label for="sort" sm={2} size="sm">Sort:</Label>
<Button onClick={props.onSubmit} size="lg" style={{ margin: '0px 5px'}} color="secondary">First Name</Button>
<Button onClick={props.onSubmit} size="lg" style={{ margin: '0px 5px'}} color="secondary">Last Name</Button>
<Button onClick={props.onSubmit} size="lg" style={{ margin: '0px 5px'}} color="secondary">ID</Button>
</Col>
</FormGroup>
</Form>
)
}
export default MyForm;
List component
import React, {Component, Fragment} from 'react';
import { Col, Form, FormGroup, Label, Input, Button } from 'reactstrap';
const List = (props) => {
return(
<Fragment>
{props.employees.map( (item, i) => (
<div style={{ margin: '40px 0px'}} key={i}>
<hr style={{ border:'1px dashed #000'}}></hr>
<div className="float-right">
<Button onClick={props.onEdit} size="lg" style={{ margin: '0px 5px'}} color="secondary">{props.Edit ? 'Save': 'Edit'}</Button>
<Button size="lg" style={{ margin: '0px 5px'}}color="secondary">Delete</Button>
</div>
<FormGroup row>
<Col sm={5}>
{props.Edit ? (
<Input type="text" onChange={props.onChange} value={ props.updatedFirst ? props.updatedFirst : item.firstName } name="updatedFirst" placeholder="Enter First Name"/>
):(
<div>
{props.updatedFirst ? props.updatedFirst : item.firstName }
</div>
)}
</Col>
</FormGroup>
<FormGroup row>
<Col sm={5}>
{props.Edit ? (
<Input type="text" onChange={props.onChange} value={ props.updatedEmployeeID ? props.updatedEmployeeID : item.employeeID} name="updatedEmployeeID" placeholder="Enter EmployeeID"/>
):(
<div>
{props.updatedEmployeeID ? props.updatedEmployeeID : item.employeeID}
</div>
)}
</Col>
</FormGroup>
<FormGroup row>
<Col sm={5}>
{props.Edit ? (
<Input type="text" onChange={props.onChange} value={ props.updatedLast ? props.updatedLast: item.lastName} name="updatedLast" placeholder="Enter Last Name"/>
):(
<div>
{props.updatedLast ? props.updatedLast : item.lastName}
</div>
)}
</Col>
</FormGroup>
</div>
))}
</Fragment>
)
}
export default List;
The following example shows how to pass a handler and set the state accordingly.
For good measure I separated the logic and the presentation, the presentational components are pure components using React.memo.
//A container should only contain the logic
class EmployeesContainer extends React.Component {
state = {
employees: [{ name: '' }, { name: '' }, { name: '' }],
};
//define what needs to happen if you click edit on an
// employee
onEdit = index => {
//edit will be called with the index of the employee
// the Employees component owns the list of employees
// so it will have to make changes to it
this.setState({
employees: this.state.employees.map((employee, i) =>
i === index ? { ...employee, edit: true } : employee
),
});
};
//Same idea as onEdit, index needs to be passed to indicate
// what employee needs to be changed
onChange = (index, e) => {
this.setState({
employees: this.state.employees.map((employee, i) =>
i === index
? { ...employee, name: e.target.value }
: employee
),
});
};
render() {
return (
<Employees
employees={this.state.employees}
onEdit={this.onEdit}
onChange={this.onChange}
/>
);
}
}
//The Employees presentational component, contains the jsx
// you can make it a pure component by using React.memo
const Employees = React.memo(
({ employees, onEdit, onChange }) => (
<div>
{employees.map((employee, index) => (
<EmployeeContainer
key={index}
index={index}
employee={employee}
onEdit={onEdit}
onChange={onChange}
/>
))}
</div>
)
);
//Make this a container as well because it does more
// than only produce jsx
class EmployeeContainer extends React.Component {
state = {};
//create onChange and onEdit only when index changes
// this will prevent unnecessary renders
static getDerivedStateFromProps(props, state) {
const { index, onChange, onEdit } = props;
if (state.index !== index) {
return {
index,
onChange: e => onChange(index, e),
onEdit: () => onEdit(index),
};
}
return null;
}
render() {
const { employee } = this.props;
const { onChange, onEdit } = this.state;
return (
<Employee
employee={employee}
onChange={onChange}
onEdit={onEdit}
/>
);
}
}
//presentational component, is also pure component
const Employee = React.memo(
({ employee, onChange, onEdit }) => (
<div>
{employee.edit ? (
<input
type="text"
value={employee.name}
onChange={onChange}
/>
) : (
<button onClick={onEdit}>edit</button>
)}
</div>
)
);
I don’t think onSubmit is updating employees correctly. You shouldn’t use this.state inside setState.
this.state inside setState ReactJS
Try this..
this.setState(prevState => ({
employees: [...prevState.employees, ourForm]
}, () => {
console.log(this.state.employees)
}))

How do I define functions in parent component and pass them to respective child components?

More of a two part question: I need to define each of the onChange handle functions in my parent component and pass them to respective child components. After that How would I get the value of form elements in your parent component?
class App extends Component {
render() {
const handleChange = (data) =>
{console.log(data)}
return (
<div className="App">
<PageOne handleChange={handleChange} />
<PageTwo />
<PageThree />
<PageFour />
<PageFive />
<PageSix />
<Button onSubmit={this.props.handleChange()}>
Submit Form
</Button>
<br/>
<br/>
</div>
);
}
}
Page One Component
class PageOne extends Component {
constructor(props){
super(props)
this.state={
generalDetails: '',
phoneNumber: '',
fName: '',
mName: '',
lName: '',
gender: '',
}
this.handleInputChange = this.handleInputChange.bind(this);
}
handleChange = (data) =>
{console.log(data)
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked :
target.value;
const name = target.name;
console.log(`Input name ${name}. Input value ${value}.`);
this.setState({
[name]: value
});
}
render() {
return (
<div className="PageOneWrapper"
style={{
background: '#969fad'
}}
>
<div className="Details">
<h1>CareerTrackers Program Orientation</h1>
<p> Please complete this form to ensure we have your most up-to-date contact details. </p>
<Form>
<TextArea
onChange={this.handleInputChange}
name="generalDetails"
placeholder='General Details'
style={{
width: 500,
minHeight: 50
}}
/>
</Form>
<br/>
<p style={{
marginRight: 600
<Input
onChange={this.handleInputChange}
name='fName'
placeholder='First Name'
/>
<Input
onChange={this.handleInputChange}
name='mName'
placeholder='Middle Name'
style={{
marginLeft: 50,
marginRight: 50
}}
/>
<Input
onChange={this.handleInputChange}
name='lName'
placeholder='Last Name'
/>
<br/><br/><br/>
<p
style={{
display: "inline",
marginRight: 480
}}
><strong>Gender: </strong>
</p>
<select
name='gender'
onChange={this.handleInputChange}
style={{
display: "inline",
}}
>
<option>Select Gender </option>
<option>Male </option>
<option>Female </option>
<option>Other </option>
</select>
<br/><br/><br/>
<p style={{
marginRight: 600
}}><strong>Email:</strong></p>
<Input
onChange={this.handleInputChange}
name='email'
placeholder='Email'
style={{
marginRight: 470,
}}
/>
<br/>
<br/>
<Input
onChange={this.handleInputChange}
name='confirmEmail'
placeholder='Confirm Email'
style={{
marginRight: 470,
}}
/>
<br/>
<br/>
<p style={{
marginRight: 540
}}><strong>Phone Number:</strong></p>
<Input
onChange={this.handleInputChange}
name='phoneNumber'
placeholder='Phone Number'
style={{
marginRight:370,
}}
/>
<select
onChange={this.handleInputChange}
name='Mobile Type'
style={{
MarginRight: 5000
}}
>
<option>Mobile Type</option>
<option>Mobile</option>
<option>Work</option>
<option>Home</option>
</select>
<br/>
<br/>
<br/><br/><br/>
</div>
</div>
);
}
}
export default PageOne;
I've summarized what you want.
There are several pages for user inputs
These pages are child of parent
Submit button and submit function is defined in parent
You want to put all input data in many pages into the submit function
How can I do this?
If above is correct, and you've not considered about React-Redux,
I highly recommend to try it.
React Redux is the official React binding for Redux. It lets your React components read data from a Redux store, and dispatch actions to the store to update data.
It would help to handle your situation.
By using it, you can store your various data into Redux store - it is central data storage
Eg. you can save many inputs into Redux store in child component, and read values from Redux store in parent component.
Here is sample code that will help you:
APP.js
import React, { Component } from 'react';
import './App.css';
import PageOne from './PageOne';
class App extends Component {
constructor(props){
super(props)
this.state={
generalDetails: 'Initial Text',
}
this.onContentChange = this.onContentChange.bind(this);
this.onSubmitForm = this.onSubmitForm.bind(this);
}
render() {
return (
<div>
<h1>{this.state.generalDetails}</h1>
<PageOne handleChange={this.onContentChange} />
<button onSubmit={this.onSubmitForm}>
Submit Form
</button>
</div>
);
}
onSubmitForm(){
//Perform all Required Logic on Form Submit
}
onContentChange(data){
console.log('in onContentChange');
console.log(data);
this.setState({
...this.state,
generalDetails: data.generalDetails
});
console.log(this.state);
}
}
export default App;
PageOne.js
import React, { Component } from 'react';
class PageOne extends Component {
constructor(props){
super(props)
this.state={
generalDetails: ''
}
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked :
target.value;
const name = target.name;
console.log(`Input name ${name}. Input value ${value}.`);
this.setState({
...this.state,
[name]: value
});
if(this.props.handleChange){
this.props.handleChange(this.state);
}
}
render() {
return <div>
<textarea
onChange={this.handleInputChange}
name="generalDetails"
placeholder='General Details'
style={{
width: 500,
minHeight: 50
}}
/>
</div>
}
}
export default PageOne;
I have added only one field as this is just sample code. You can updated code based on your requirements.

Value property on input field changes after a second user interaction - React

I have a form with two input elements - one for name (type="text") and one for age (type="number"). I manage the form state in the parent component. I have my handler methods and everything set up. The form state changes when user types. Also the input value property on the name field receives its value based on the current state, but the number field does not receive it while user types but on a second interaction with the page (a click somewhere else let's say)
Here is my Form component:
const Form = (props) => {
return (
<div className="col-md-4 form">
<form id="myForm">
<div className="form-group">
<label>Please, enter your name</label>
<input onChange={props.inputChanged} id="name" type="text" className="form-control" name="name"
placeholder="Name" value={props.data.name}/>
</div>
<div className="form-group">
<label>How old are you?</label>
<input onChange={props.inputChanged} id="age" type="number" className="form-control" name="age"
placeholder="Age" value={props.data.age}/>
</div>
</div>
And the parent class based component which owns the state:
class DbControls extends Component {
//INITIAL STATE
state = {
name: '',
age: '',
greeting: 'hello',
role: 'admin'
}
//HANDLE USER INPUT FOR NAME AND AGE
inputHandler = (e) => {
if (e.target.id === 'name') {
this.setState({name: e.target.value})
} else if (e.target.id === 'age') {
this.setState({age: e.target.value})
}
}
//HANDE USER PREFERENCES FOR GREETING AND ROLE
selectHandler = (e) => {
if (e.target.id === 'greet') {
this.setState({greeting: e.target.value})
} else if (e.target.id === 'role') {
this.setState({role: e.target.value})
}
}
render() {
return (
<div className="container-fluid">
<div className="row">
<Form data={this.state} inputChanged={this.inputHandler} selectChanged={this.selectHandler}/>
<div className="col-md-6 table">
<Table tableFor="Admins table"/>
<Table tableFor="Moderators table"/>
</div>
</div>
</div>
);
}
}
I am not sure if this is an actual problem but I was just curious what might be the reason for that behavior?
As per some of your comments i understand that the real issue you are facing is the the DOM value attribute is not in sync with the value property.
This is by design, there is a lot of talk about this issue and it mostly related to the confusion people do with:
jsx value attribute / property
DOM (devtools html) value attribute
These are not the same thing.
There is another thing to consider, related to passwords exploit.
You can read this issue for better understanding and more details.
Simplify how the inputs are setting state based upon the input's e.target.name and e.target.value. By design, the DOM number input value will only update when unfocused, but state is always up-to-date.
Here's a working example: https://codesandbox.io/s/qzl5knl4kj
formInputs.js
import React, { Fragment } from "react";
export default ({ age, handleChange, name }) => (
<Fragment>
<label>Please, enter your name: </label>
<br />
<input
onChange={handleChange}
type="text"
className="uk-input"
name="name"
placeholder="Name"
value={name}
style={{ width: 300, marginBottom: 10 }}
/>
<br />
<label>How old are you?</label>
<br />
<input
onChange={handleChange}
type="number"
className="uk-input"
name="age"
placeholder="Age"
value={age}
style={{ width: 300, marginBottom: 10 }}
/>
<br />
</Fragment>
);
index.js
import React, { Component } from "react";
import { render } from "react-dom";
import FormInputs from "./formInputs";
import "uikit/dist/css/uikit.min.css";
import "./styles.css";
class App extends Component {
state = {
name: "",
age: "",
greeting: "hello",
role: "admin"
};
handleChange = ({ target: { name, value } }) => this.setState({ [name]: value });
handleFormClear = () => this.setState({ age: "", name: "" });
handleSubmit = e => {
e.preventDefault();
const { age, name } = this.state;
if (!age || !name) return;
alert(`Name: ${name}, Age: ${age}`);
};
render = () => (
<form onSubmit={this.handleSubmit} style={{ textAlign: "center" }}>
<h1>Mixed Input Fields</h1>
<FormInputs {...this.state} handleChange={this.handleChange} />
<button
style={{ marginBottom: 20, marginRight: 10 }}
type="submit"
className="uk-button uk-button-primary"
>
Submit
</button>
<button
style={{ marginBottom: 20 }}
type="button"
className="uk-button uk-button-danger"
onClick={this.handleFormClear}
disabled={!this.state.name && !this.state.age}
>
Clear
</button>
<div>
<pre style={{ margin: "auto", width: 300 }}>
<code>
Name: {this.state.name}
<br />
Age: {this.state.age}
</code>
</pre>
</div>
</form>
);
}
render(<App />, document.getElementById("root"));

Resources