How do I reset modal props on close - reactjs

I have a component, DeleteRoute, which is just a wrapper around a Modal. It allows me to delete a "Route" entity.
I am using React as well as Redux to maintain data and state.
I am struggling with the correct architecture to make the modal work. I didn't like having all of the state in the parent container because it made the parent cluttered. So I put the redux actions inside the modal. This way I pass in a route from the parent, but call "deleteRoute" from the redux store inside the DeleteRoute component, and I can display success and error messages.
This all works great, except it I close and re-open the modal, the previous success/error message is still displayed. This is because the closing/opening is done with a toggle from the parent, but the parent can't reset the child props.
I thought I could just pass the success and error properties in the parent, and anytime the parent re-renders it would reset those, but it isn't (despite it re-rendering when isOpen changes).
class DeleteRoute extends React.Component {
constructor(props) {
super(props);
this.deleteRoute = this.deleteRoute.bind(this);
}
deleteRoute() {
this.props.deleteRoute(this.props.route);
}
render() {
var route = this.props.route || {};
return (
<div>
<Modal
isOpen={this.props.isOpen}
toggle={this.props.toggle}
>
<ModalHeader toggle={this.props.toggle}>Delete Route</ModalHeader>
<ModalBody>
{this.props.isLoading && <Spinner color="primary" />}
<Alert color="danger" isOpen={this.props.error}>{this.props.error}</Alert>
<Alert color="success" isOpen={this.props.success}>Deleted successfully</Alert>
<Form>
<Row form>
<Col>
<FormGroup>
<Label for="CssPlatform">Css Platform</Label>
<Input disabled name="CssPlatform" type="text" value={route.CSSPlatform} />
</FormGroup>
</Col>
<Col>
<FormGroup>
<Label for="ProdManager">Prod Manager</Label>
<Input disabled name="ProdManager" type="text" value={route.ProdManager} />
</FormGroup>
</Col>
</Row>
<Row form>
<Col>
<FormGroup>
<Label for="CssProduct">Css Product</Label>
<Input disabled name="CssProduct" type="text" value={route.CSSProduct} />
</FormGroup>
</Col>
<Col>
<FormGroup>
<Label for="ProgSupervisor">Prog Supervisor</Label>
<Input disabled name="ProgSupervisor" type="text" value={route.ProgSupervisor} />
</FormGroup>
</Col>
</Row>
<Row form>
<Col>
<FormGroup>
<Label for="CssSubProduct">Css SubProduct</Label>
<Input disabled name="CssSubProduct" type="text" value={route.CSSSubProduct} />
</FormGroup>
</Col>
<Col>
<FormGroup>
<Label for="RallyProject">Rally Project</Label>
<Input disabled name="RallyProject" type="text" value={route.RallyProject} />
</FormGroup>
</Col>
</Row>
<Row form>
<Col>
<FormGroup check inline>
<Label check>
<Input disabled name="CssProductActive" type="checkbox" checked={route.CSSProductActive} />
Css Product Active
</Label>
</FormGroup>
</Col>
<Col>
<FormGroup check inline>
<Label check>
<Input disabled name="CssSubProductActive" type="checkbox" checked={route.CSSSubProductActive} />
Css SubProduct Active
</Label>
</FormGroup>
</Col>
</Row>
</Form>
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={this.deleteRoute}>Delete Route</Button>{' '}
<Button color="secondary" onClick={this.props.toggle}>Cancel</Button>
</ModalFooter>
</Modal>
</div>
);
}
}
export default connect(
state => state.deleteRouteReducer,
dispatch => bindActionCreators(actionCreators, dispatch))(DeleteRoute);
render() {
return (
<div>
<h2>Routes</h2>
<p>Routes define how items from CSS get imported into Rally. Use routes to connect a Rally project to a set of criteria in CSS.</p>
<div>
<AddRoute isOpen={this.props.showAddRoute} toggle={this.toggleAddRoute} />
<DeleteRoute error={this.props.deleteRouteError} success={this.props.deleteRouteSuccess} isOpen={this.props.showDeleteRoute} route={this.props.selectedRoute} toggle={this.toggleDeleteRoute} />
<DataTable
actions={[
{ Click: this.toggleAddRoute, Color: 'green', Icon: 'MdAdd', ToolTip: "Add new route" },
{ Click: this.toggleEditRoute, Color: 'orange', Icon: 'MdEdit', ToolTip: "Edit route", RowAction: true },
{ Click: this.toggleDeleteRoute, Color: 'red', Icon: 'MdRemove', ToolTip: "Delete route", RowAction: true },
]}
columns={[
{ Title: "Platform", Field: "CSSPlatform" },
{ Title: "Product", Field: "CSSProduct" },
{ Title: "SubProduct", Field: "CSSSubProduct" },
{ Title: "ProdManager", Field: "ProdManager" },
{ Title: "ProgSupervisor", Field: "ProgSupervisor" },
{ Title: "Product Active?", Field: "CSSProductActive" },
{ Title: "SubProduct Active?", Field: "CSSSubProductActive" },
{ Title: "Rally Project", Field: "RallyProject" },
{ Title: "Rally Url", Field: "RallyUrl" }
]}
data={this.props.routes}
edit={this.editRoute}
isLoading={this.props.isLoading} />
</div>
</div>
);

I ended up adding a redux action to reset the success and error props, and then calling that when the modal is closed. I think this is in line with correct redux architecture, but I'm open to better solutions if anyone has them.
class DeleteRoute extends React.Component {
constructor(props) {
super(props);
this.deleteRoute = this.deleteRoute.bind(this);
this.toggle = this.toggle.bind(this);
}
deleteRoute() {
this.props.deleteRoute(this.props.route);
}
toggle() {
if (this.props.isOpen) {
// reset the error and success messages on close
this.props.initialize();
}
this.props.toggle();
}
render() {
var route = this.props.route || {};
return (
<div>
<Modal
isOpen={this.props.isOpen}
toggle={this.toggle}
>
<ModalHeader toggle={this.toggle}>Delete Route</ModalHeader>
<ModalBody>
{this.props.isLoading && <Spinner color="primary" />}
<Alert color="danger" isOpen={this.props.error}>{this.props.error}</Alert>
<Alert color="success" isOpen={this.props.success}>Deleted successfully</Alert>
<Form>
<Row form>
<Col>
<FormGroup>
<Label for="CssPlatform">Css Platform</Label>
<Input disabled name="CssPlatform" type="text" value={route.CSSPlatform} />
</FormGroup>
</Col>
<Col>
<FormGroup>
<Label for="ProdManager">Prod Manager</Label>
<Input disabled name="ProdManager" type="text" value={route.ProdManager} />
</FormGroup>
</Col>
</Row>
<Row form>
<Col>
<FormGroup>
<Label for="CssProduct">Css Product</Label>
<Input disabled name="CssProduct" type="text" value={route.CSSProduct} />
</FormGroup>
</Col>
<Col>
<FormGroup>
<Label for="ProgSupervisor">Prog Supervisor</Label>
<Input disabled name="ProgSupervisor" type="text" value={route.ProgSupervisor} />
</FormGroup>
</Col>
</Row>
<Row form>
<Col>
<FormGroup>
<Label for="CssSubProduct">Css SubProduct</Label>
<Input disabled name="CssSubProduct" type="text" value={route.CSSSubProduct} />
</FormGroup>
</Col>
<Col>
<FormGroup>
<Label for="RallyProject">Rally Project</Label>
<Input disabled name="RallyProject" type="text" value={route.RallyProject} />
</FormGroup>
</Col>
</Row>
<Row form>
<Col>
<FormGroup check inline>
<Label check>
<Input disabled name="CssProductActive" type="checkbox" checked={route.CSSProductActive} />
Css Product Active
</Label>
</FormGroup>
</Col>
<Col>
<FormGroup check inline>
<Label check>
<Input disabled name="CssSubProductActive" type="checkbox" checked={route.CSSSubProductActive} />
Css SubProduct Active
</Label>
</FormGroup>
</Col>
</Row>
</Form>
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={this.deleteRoute}>Delete Route</Button>{' '}
<Button color="secondary" onClick={this.toggle}>Cancel</Button>
</ModalFooter>
</Modal>
</div>
);
}
}
export default connect(
state => state.deleteRouteReducer,
dispatch => bindActionCreators(actionCreators, dispatch))(DeleteRoute);
and the redux bits
import axios from 'axios';
// actions
const deleteRouteType = "DELETE_ROUTE";
const deleteRouteFailureType = "DELETE_ROUTE_FAILURE";
const deleteRouteSuccessType = "DELETE_ROUTE_SUCCESS";
const initializeType = "DELETE_ROUTE_INITIALIZE";
const initialState = { error: null, success: null };
// action creators
export const actionCreators = {
initialize: () => (dispatch) => {
dispatch({ type: initializeType });
},
deleteRoute: (route) => async (dispatch) => {
dispatch({ type: deleteRouteType });
axios
.delete(`api/route`, route)
.then(res => {
if (res.data.error) {
dispatch({ type: deleteRouteFailureType, payload: res.data.errorMessage });
}
else {
dispatch({ type: deleteRouteSuccessType, payload: res.data.data });
}
})
.catch(err => {
dispatch({ type: deleteRouteFailureType, payload: err.message });
});
}
};
// reducers
export const reducer = (state, action) => {
state = state || initialState;
switch (action.type) {
case initializeType:
return {
...state,
error: null,
isLoading: false,
success: false
};
case deleteRouteType:
return {
...state,
error: null,
isLoading: true,
success: false
};
case deleteRouteFailureType:
return {
...state,
error: action.payload,
isLoading: false,
success: false
};
case deleteRouteSuccessType:
return {
...state,
error: null,
isLoading: false,
success: true
};
default:
return state;
}
};

Related

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 to call OnChange function in react using withformik with antd component?

Here I'm calling onChange function on Formik Field but its not calling? How to call custom function on a Formik field?
This is my custom function under React Component:
onStudentScore = (value, form) => {
alert("called");
const maxScore = value.writtenexammaxscore;
console.log(maxScore);
form.getFieldValue("writtenexammaxscore", maxScore);
if (maxScore > form.getFieldValue("writtenexamstudentsscore")) {
alert("MaxScore is less than StudentScore");
}
};
And my Form is created under render and write a onChange function on a StudentScore field. But it's not called? How to call this function?
render() {
const { values, handleSubmit} = this.props
return (
return (
<div>
<h5 align="left">MidTerm Form</h5>
<Card>
<Form onSubmit={handleSubmit}>
<Row>
<Col span={4}>
<b>Written Exam:</b>
</Col>
<Col span={2}>
<Field
name="writtenexammaxscore"
component={AntInput}
type="text"
style={{ width: 40 }}
/>
</Col>
<Col span={2}>outof</Col>
<Col span={3}>
<Field
name="writtenexamstudentsscore"
component={AntInput}
type="text"
style={{ width: 40 }}
onChange={this.onStudentScore}
/>
// I wrote the function on field this way
</Col>
<Col span={2}>
<Divider type="vertical" />
</Col>
</Row>
<Row>
<Col span={10} />
<Col span={8} push={10}>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Col>
</Row>
</Form>
</Card>
</div>
);
}
const MidTermForm = withFormik({
mapPropsToValues: () => ({
writtenexammaxscore: '',
writtenexamstudentsscore: '',
oralexammaximumscore: '',
oralexamstudentsscore: '',
}),
handleSubmit(values, { resetForm }) {
resetForm();
console.log(values)
}
})(MidTermFormComponent)
export default MidTermForm
I tried by extending yup validation schema. Instead of calling a function in onChange
check this code sandbox

How to independently delete dynamically-added input fields in ReactJS

I'm trying to independently delete dynamic inputs in a form in React. I have a user attribute group, and then user attribute children. I need to be able to dynamically add new user attribute groups and children, but then delete those fields without deleting ALL of the child attributes.
Right now, when I delete a child attribute, it deletes one from EACH user attribute group.
I have a working fiddle here that shows my code: https://codesandbox.io/embed/23kr654w80
import React, { Component } from "react";
import { Button, Input, Row, Col, Form, FormGroup, Label } from "reactstrap";
class OfferCriteria extends Component {
constructor(props) {
super(props);
this.state = {
single: "",
attributeSingle: [{ single: "" }],
child: "",
attributeChild: [{ child: " " }]
};
}
handleNameChange = event => {
this.setState({
name: event.target.value
});
};
handleAddSingleAttribute = () => {
this.setState({
attributeSingle: this.state.attributeSingle.concat([{ name: "" }])
});
};
handleRemoveSingleAttribute = idx => () => {
this.setState({
attributeSingle: this.state.attributeSingle.filter(
(s, sidx) => idx !== sidx
)
});
};
handleAddChildAttribute = () => {
this.setState({
attributeChild: this.state.attributeChild.concat([{ child: "" }])
});
};
handleRemoveChildAttribute = idz => () => {
this.setState({
attributeChild: this.state.attributeChild.filter(sidz => idz !== sidz)
});
};
render() {
return (
<div>
<Row>
<Col lg="10">
<hr />
</Col>
<Col lg="2" className="float-right">
<Button color="success" onClick={this.handleAddSingleAttribute}>
Add Attribute Group
</Button>
</Col>
</Row>
{this.state.attributeSingle.map(() => (
<div>
<br />
<Row>
<Col lg="2">
<Label>User Attributes</Label>
</Col>
<Col lg="3" className="float-left">
<FormGroup check inline>
<Input
className="form-check-input"
type="radio"
id="includeUserAttributes"
name="inline-radios"
value="includeUserAttributes"
/>
<Label
className="form-check-label"
check
htmlFor="inline-radio1"
>
Include
</Label>
</FormGroup>
<FormGroup check inline>
<Input
className="form-check-input"
type="radio"
id="excludeUserAttributes"
name="inline-radios"
value="excludeUserAttributes"
/>
<Label
className="form-check-label"
check
htmlFor="inline-radio2"
>
Exclude
</Label>
</FormGroup>
</Col>
<Col lg="4">
<Input
type="text"
name="text-input"
placeholder="This is parent attribute"
/>
</Col>
</Row>
<br />
<Row>
<Col lg="3">
{this.state.attributeChild.map(() => (
<div className="shareholder">
<Input
type="text"
name="text-input"
placeholder="This is child attribute"
/>
</div>
))}
</Col>
<Col lg="3" className="float-right">
{this.state.attributeChild.map(() => (
<div className="shareholder">
<Button
color="primary"
onClick={this.handleAddChildAttribute}
>
Add Attribute Child
</Button>
<br />
</div>
))}
</Col>
<Col lg="3" className="float-right">
{this.state.attributeChild.map(idz => (
<div className="shareholder">
<Button
color="danger"
onClick={this.handleRemoveChildAttribute(idz)}
>
Remove Attribute Child
</Button>
<br />
</div>
))}
</Col>
</Row>
<hr />
</div>
))}
</div>
);
}
}
export default OfferCriteria;
I need these child attributes to delete ONLY in their parent attribute group, instead of deleting all of them from all the attribute groups.
There are a couple of things going wrong with your code, but I'll focus on your initial question.
The problem is that you use the same array of child for all your groups. In order to be correct, you should include the attributeChild state into the attributeSingle objects :
{
attributeSingle: [
{
single: "",
attributeChild: [
{
child: " "
}
]
}
]
}
That way, children remain independent between groups.

react+redux with react-bootstrap > FormControl > get value

I am using react+redux with react-bootstrap components.
I would like to pass the value of a FormControl text element (email) to the dispatched redux action but I do not know how to do that.
class LoginForm extends React.Component {
render() {
const email = React.findDOMNode(this.refs.email);
return (
<div>
<Form horizontal>
<FormGroup controlId="formHorizontalEmail">
<Col componentClass={ControlLabel}>Email</Col>
<Col><FormControl type="email" ref="email"/></Col>
</FormGroup>
<FormGroup>
<Col>
<Button type="submit" block>Sign in</Button>
</Col>
</FormGroup>
</Form>
<Button onClick={() => this.props.doLogin(email, 'password')}>Login</Button>
</div>
)
}
}
/**
* Connect staff.
*/
const mapStateToProps = (state) => {
return {
...
};
};
const mapDispatchToProps = (dispatch) => {
return {
doLogin: (email, password) => dispatch(performLogin(email, password))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(LoginForm)
The only one way how to read text value of a FormControl with React (according to my research) is this:
class LoginForm extends React.Component {
handleOnChange = (event) => {
this.setState({ [event.target.id]: event.target.value }, null);
}
render() {
return (
<div>
<Form horizontal>
<FormGroup controlId="email">
<Col componentClass={ControlLabel}}>Email</Col>
<Col>
<FormControl type="email" placeholder="Email" onChange={this.handleOnChange}
/>
</Col>
</FormGroup>
<FormGroup controlId="password">
<Col componentClass={ControlLabel} sm={2}>Password</Col>
<Col>
<FormControl type="password" placeholder="Password" onChange={this.handleOnChange} />
</Col>
</FormGroup>
<FormGroup>
<Col>
<Button onClick={() => this.props.doLogin(this.state.email, this.state.password)}>Submit</Button>
</Col>
</FormGroup>
</Form>
</div>
)
}
}

ReduxForm inside Modal

I use ReactJS and want to add reduxForm to modal.
For modal I use reactstrap library that represents bootstrap 4 for React.
For form validation I use reduxForm
I have tasked add form modal with validation fields and I did that:
render() {
const {handleSubmit, fields: {
email
}} = this.props;
const FormEmail = ({touched, error}) => {
if (touched && error)
return (
<div>
<Input type="email" name="email" id="email"/>
<div className="error">{error}</div>
</div>
);
return (<Input type="email" name="email" id="email"/>);
}
return (
<div>
<Col>
<Col xs={{
size: 9
}}>
<ReportSettings/>
</Col>
<Col xs={{
size: 3
}}>
<Button outline color="primary" onClick={this.toggle}>Share</Button>
</Col>
</Col>
<Form onSubmit={handleSubmit(this.toggle)}>
<Modal isOpen={this.state.modal} toggle={this.toggle} className={this.props.className}>
<ModalHeader toggle={this.toggle}>Share</ModalHeader>
<ModalBody>
<FormGroup row>
<CheckBoxInputField data={this.state.measurements} handleFieldChange={this.handleFieldChange}/>
</FormGroup>
<FormGroup row>
<Label for="email" xs={2}>Email</Label>
<Col xs={10}>
<FormEmail {...email}/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="message" xs={2}>Message</Label>
<Col xs={10}>
<Input type="textarea" name="message" id="message"/>
</Col>
</FormGroup>
</ModalBody>
<ModalFooter>
<Button action="submit" color="primary" value={true}>OK</Button>
<Button color="secondary" onClick={this.toggle} value={false}>Cancel</Button>
</ModalFooter>
</Modal>
</Form>
</div>
);
}
my toggle function:
toggle(e) {
e.preventDefault();
this.setState({
modal: !this.state.modal
});
if (e.target.value === 'true') {
const measurementsID = this.state.measurements.values.filter((measurement) => {
return measurement.checked;
}).map((measurement) => {
return measurement.value;
});
this.props.onShare(MeasurementsToBitMap(measurementsID));
}
}
validation function:
function validate(formProps) {
const errors = {};
if (!formProps.email) {
errors.email = 'Please enter an email';
}
return errors;
}
finally I export Component:
export default reduxForm({form: 'form', fields: ['email'], validate})(HeadMeasurement);
When I click on cancel button the modal close it works good, but when I click on OK button
and fields aren't valid error messages aren't showed otherwise when fields are valid modal isn't disappeared.
My question is how to combine reduxForm and Modal work together?
Thanks, Michael.
reactstrap inserts your modal definition at the bottom of the rendered html doc so wrapping <Form> around <Modal> doesn't work. You have to have your inside the . In your case since you are doing a submit action you will want it wrapped around both your footer and the body tags.
<Modal isOpen={this.state.modal} toggle={this.toggle} className={this.props.className}>
<Form onSubmit={handleSubmit(this.toggle)}>
<ModalHeader toggle={this.toggle}>Share</ModalHeader>
<ModalBody>
<FormGroup row>
<CheckBoxInputField data={this.state.measurements} handleFieldChange={this.handleFieldChange}/>
</FormGroup>
<FormGroup row>
<Label for="email" xs={2}>Email</Label>
<Col xs={10}>
<FormEmail {...email}/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="message" xs={2}>Message</Label>
<Col xs={10}>
<Input type="textarea" name="message" id="message"/>
</Col>
</FormGroup>
</ModalBody>
<ModalFooter>
<Button action="submit" color="primary" value={true}>OK</Button>
<Button color="secondary" onClick={this.toggle} value={false}>Cancel</Button>
</ModalFooter>
</Form>
</Modal>
Also, did you bind your toggle to this in your constructor?
constructor(props) {
this.toggle = this.toggle.bind(this);
}
Alternatively, use arrow functions.
toggle = (e) => {
e.preventDefault();
this.setState({
modal: !this.state.modal
});
...
}

Resources