React - Close Modal on Form Submit - reactjs

New to React. I am trying to find out how I can close a Modal after the Form Submit event has been triggered.
export default class UserAdmin extends Component {
constructor(props) {
super(props);
this.state = {
show_user_modal : false
}
}
// Handle User Modal
handleUserModalOpen = () => {
this.setState({ show_user_modal: true});
}
handleUserModalClose = () => {
this.setState({ show_user_modal: false});
}
render() {
const { show_user_modal } = this.state;
return (
<Content>
<div className="site-layout-background">
<div className="contentBody">
<Button type="primary"onClick={this.handleUserModalOpen}>
Add User
</Button>
{show_user_modal && <AddUserModal handleClose={this.handleUserModalClose}/>}
</div>
</div>
</Content>
)
}
}
This works perfectly to open and close the modal, and the submit is working perfectly inside the addUserModal, however I am unsure how I should close the modal after this has been completed. I have tried to setState() from the parent to the child but it doesn't want to even then show the modal. Any help appreciated!
**Adding addUserModal function:
function AddUserModal({handleClose}){
const [addUserForm] = Form.useForm();
/** POST User */
const postUser = (values) => {
axios.post('http://localhost:5000/portal/add-user', values)
.then(res => {
if (res.status === 200) {
console.log(res.data);
}
})
};
return(
<Modal title="Add User" okText="Confirm" visible={true} onCancel={handleClose}
onOk={() => {
addUserForm
.validateFields()
.then((values) => {
postUser(values);
addUserForm.resetFields();
})
.catch((info) => {
console.log('Validate Failed:', info);
});
}}
>
<Form
form={addUserForm}
name="addUserForm"
labelCol={{span: 5,}}
wrapperCol={{span: 16,}}
initialValues={{remember: false,}}
>
<Form.Item label="Username" name="username"
rules={[
{
required: true,
message: 'Please input a username!',
},
]}
>
<Input />
</Form.Item>
<Form.Item label="Email" name="email"
rules={[
{
required: true,
message: 'Please input an email address',
},
]}
><Input />
</Form.Item>
<Form.Item label="Password" name="password"
rules={[
{
required: true,
message: 'Please input a password',
},
]}
><Input.Password />
</Form.Item>
</Form>
</Modal>
);
}
export default AddUserModal;

Your modal has visible property always set to true. Pass show_user_modal variable to the child and use it in the modal visiblestate. The {show_user_modal && <AddUserModal... is unnecessary

handleClose is calling handleUserModalClose. I think the issue is something else. So you can try calling handleClose in .then of your API call and pass the visible prop as well
export default class UserAdmin extends Component {
constructor(props) {
super(props);
this.state = {
show_user_modal : false
}
}
// Handle User Modal
handleUserModalOpen = () => {
this.setState({ show_user_modal: true});
}
handleUserModalClose = () => {
this.setState({ show_user_modal: false});
}
render() {
const { show_user_modal } = this.state;
return (
<Content>
<div className="site-layout-background">
<div className="contentBody">
<Button type="primary"onClick={this.handleUserModalOpen}>
Add User
</Button>
{show_user_modal && <AddUserModal visible={show_user_modal} handleClose={this.handleUserModalClose}/>}
</div>
</div>
</Content>
)
}
}
and use it in AddUserModal
function AddUserModal({visible, handleClose}){
const [addUserForm] = Form.useForm();
/** POST User */
const postUser = (values) => {
axios.post('http://localhost:5000/portal/add-user', values)
.then(res => {
if (res.status === 200) {
console.log(res.data);
handleClose();
}
})
};
return(
<Modal title="Add User" okText="Confirm" visible={visible} onCancel={handleClose}
onOk={() => {
addUserForm
.validateFields()
.then((values) => {
postUser(values);
addUserForm.resetFields();
})
.catch((info) => {
console.log('Validate Failed:', info);
});
}}
>
<Form
form={addUserForm}
name="addUserForm"
labelCol={{span: 5,}}
wrapperCol={{span: 16,}}
initialValues={{remember: false,}}
>
<Form.Item label="Username" name="username"
rules={[
{
required: true,
message: 'Please input a username!',
},
]}
>
<Input />
</Form.Item>
<Form.Item label="Email" name="email"
rules={[
{
required: true,
message: 'Please input an email address',
},
]}
><Input />
</Form.Item>
<Form.Item label="Password" name="password"
rules={[
{
required: true,
message: 'Please input a password',
},
]}
><Input.Password />
</Form.Item>
</Form>
</Modal>
);
}
export default AddUserModal;

Related

React state sets the same value to all of the state values

I'm new to react JS, I'm trying to get the values from a form, and send it to a node JS middleware.
I set up two values in the state, one for the email and another one for the password. I set the state for both values in the set state method.
class LoginForm extends Component {
constructor(props){
super(props)
this.state = {
email : '',
password : '',
}
}
handleChange = (e) => {
this.setState({ email : e.target.value, password : e.target.value})
}
handleSubmit = (e) => {
console.log('state', this.state)
};
render () {
return (
<div style = {styles.form} >
<Fragment>
<Form
{...layout}
name="basic"
initialValues={{
remember: true,
}}
onFinish={this.handleSubmit}
>
<Form.Item
name="email"
rules={[
{
type: 'email',
message: 'The input is not valid E-mail!',
},
{
required: true,
message: 'Please input your E-mail!',
},
]}
hasFeedback
>
<Input
placeholder={t`Email`}
value={this.state.email}
onChange={this.handleChange} />
</Form.Item>
<Form.Item
name="password"
rules={[{ required: true }]} hasFeedback
>
<Input.Password
placeholder={t`Password`}
value={this.state.password}
onChange={this.handleChange}
/>
</Form.Item>
<Button
type="primary"
htmlType="submit"
>
<span style = {styles.button} >Sign in</span>
</Button>
</Form>
</Fragment>
</div>
)
}
}
}
I created the handle submit function and linked it to the onsubmit method inside the form and tried console logging the current state of my values inside the handle submit function. To my surprise the value from the password gets console logged for the value email too. Like this
state {email: "123", password: "123"}
I cannot figure out what am I doing wrong.
I think if you change your handleChange function to this, it should work.
handleChange = (e) => {
this.setState({ [e.target.id] : e.target.value})
}
And add id to the input fields like this
<Input id="email" placeholder={t`Email`} value={this.state.email} onChange {this.handleChange} />
<Input.Password id="password" placeholder={t`Password`} value {this.state.password} onChange={this.handleChange} />
Here is the solution:
handleChange = (e) => {
let email = ''
let password = ''
if (e.target.name === 'email') {
email = e.taget.value
} else {
password = e.taget.value
}
this.setState({ email, password})
}

Writing jest tests for antd FormItem

I am trying to write jest tests for my component which uses antd that uses Form. Pasting a snippet of the code.
Component.tsx:
render() {
const { Option } = Select;
return (
<div>
<Form
ref={this.formRef}
{...formItemLayout}
onFinish={(values) => {this.props.onSubmit(values)}
>
<Form.Item
name="foo"
label="Foo"
hasFeedback
rules={[
{ required: true, message: 'Foo is required' },
() => ({
validator(rule, value) {
if(!isUniqueFoo(value)) {
return Promise.reject(new Error('wrong foo'));
}
if (value.length > 10) {
return Promise.reject(new Error('too many characters'));
}
return Promise.reject(new Error('something went wrong')));
}
})
]}
>
<Input type="text" />
</Form.Item>
<Form.Item
name="bar"
label="Bar"
hasFeedback
rules={[
{ required: true, message: 'bar is required' },
() => ({
validator(rule, value) {
if (isValidInput(value)) {
return Promise.resolve();
}
return Promise.reject(new Error('wrong value')));
}
})
]}
>
<Input />
</Form.Item>
<Form.Item
name="baz"
label="Baz"
>
<Select
showSearch
className="select-dropdown"
optionFilterProp="children"
onChange={this.onChange}
>
{data.map((d: Data) => (
<Option key={d.id} value={d.id}>{d.name}</Option>
))
}
</Select>
</Form.Item>
</>
)}
</Form>
</div>
}
Trying to write Jest tests for this above component.
This is a test that works:
it('test', () => {
const onSubmit = jest.fn();
const wrapper = shallow(<Component {...defaultProps} onSubmit={onSubmit} />);
wrapper.find('ForwardRef(InternalForm)').props().onFinish(values);
expect(onSubmit).toHaveBeenCalledWith({
...defaultProps.baz,
// Changed items.
foo: 'test',
bar: 'other test'
});
});
While the above test works, I would like to test other things like validations. None of these code snippets work. I am trying to test whether the field 'Foo' is entered and the length of text is < 10 chars, and is validated, etc.
console.log("test1", wrapper.find('ForwardRef(InternalForm)').shallow().find('Input'));
console.log("test2", wrapper.find('ForwardRef(InternalForm)').find('Input'));
console.log("test3", wrapper.find('Input'));
Another thing that irks me is having to use wrapper.find('ForwardRef(InternalForm)') instead of wrapper.find('Form')
Any thoughts?

How to fix update Modal not showing this.state.value

I am trying to create a modal that handles updates, but it's not showing {this. state. value}
state={
name: '',
businessName: '',
show: false,
}
showModal(e) {
if (e) {
this.setState({ show: true });
}
}
showEditModal(e) {
const { user } = this.props.auth;
const email=user.email
axios.get(`/api/v1/auth/userdetails/${email}`)
.then(
(res) => {
console.log(res.data) // res.data actually has data from API
let user = res.data
this.setState ({
id:user.id,
name:user.name,
businessName:user.businessName,
})
this.setState({
show: true,
});
console.log(this.state.name) // i get undefined
},
(err) => {
alert('An error occured! Try refreshing the page.', err);
}
);
}
onChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
};
onSubmit = (e) => {
e.preventDefault();
const {
_id,
name,
businessName,
} = this.state;
const userDetail = {
_id,
name,
businessName,
};
console.log(userDetail);
axios.post(`/api/v1/auth/update/${_id}`, userDetail).then(
(res) => {
Swal.fire({
title: 'Profile ',
text: 'Update successful!',
icon: 'success',
confirmButtonText: 'Ok'
})
},
(err) => {
alert('An error occured! Try submitting the form again.', err);
}
);
};
hideModal = () => {
this.setState({
show: false,
name:'',
businessName:'',
});
};
ModalForm=()=>{
return <div style={{ backgroundColor: 'white' }}>
<Modal
{...this.props}
show={this.state.show}
onHide={this.hideModal}
dialogClassName='custom-modal'
>
<Modal.Header closeButton>
<Modal.Title
id='contained-modal-title-lg '
className='text-center'
>
Update Profile
</Modal.Title>
</Modal.Header>
<Modal.Body className='modal-contentx'>
<Form horizontal onSubmit={this.onSubmit}>
<FormGroup controlId='formHorizontalEmail'>
<Col smOffset={4} sm={6}>
<FormControl
type='Text'
backgroundColor='grey'
placeholder='name'
name='name'
value={this.state.name}
onChange={this.onChange}
/>
</Col>
</FormGroup>
<FormGroup controlId='formHorizontalPassword'>
<Col smOffset={4} sm={6}>
<FormControl
type='text'
placeholder=''
name='businessName'
value={this.state.businessName}
onChange={this.onChange}
/>
</Col>
</FormGroup>
<FormGroup>
<Col smOffset={5} sm={6}>
<Button type='submit' bsStyle='primary'>
Add
</Button>
</Col>
</FormGroup>
</Form>
</Modal.Body>
</Modal>
</div>
<ButtonGroup>
<Button className="btn-fill" color="primary" type="submit">
Save
</Button>
<Button className="btn-fill" onClick={(e) => this.showEditModal(e)}>
Update
</Button>
</ButtonGroup>
}
render(){
return (
<div>
// Add form is placed here
</div>
<div>
// modal form is placed here
{this.ModalForm()}
</div>
)
}
the modal comes up onClick, but fails to show any value. When i do console.log(this.state.name) for instance i get undefined?!
I need the Modal to be populated with the value from the state. The response object from the console contains the data that i am using to update the state through setState().
i guess something is off though, but cant figure it out

React Redux Form select dropdown not binding data from api

On click of edit from sending data from one component to other in first component i have a table displaying list of values and edit button on click of edit button it navigates to component where it has a redux form list of form fields which contains input text and select dropdown input text is binding but not select field state getting value not able to bind.'
This state in my component and dropdown
this.state = {
toWidget:false,
typeOfChart: '',
widgetName: '',
loader: false,
dropdownData: [
{ name:"Choose any Chart", value:"choose" },
{ name:"Bar Chart", value:"bar" },
{ name:"Pie Chart", value:"pie" },
{ name:"Column Chart", value:"column" }
],
}
getting data from other component & initializing here:
componentDidMount() {
if (this.props.match.params.id) {
this.widgetApiService.getWidgets().then(data => {
const getEditObj = data.filter(widget => (widget.id == this.props.match.params.id))
if (getEditObj) {
getEditObj.map(data => (
this.props.initialize({
widgetName: data.name,
typeOfChart: data.basicsettings.chart.type
})
))
this.setState({typeOfChart: getEditObj[0].basicsettings.chart.type});
this.setState({defaultVal: getEditObj[0].basicsettings.chart.type});
}
})
}
}
renderSelect = ({ input,select, placeholder, type, meta, defaultValue }) => {
return (
<div>
<Form.Control as="select" {...select} {...input} type={type} placeholder={placeholder} >
{
this.state.dropdownData.map((item,index) => (
<option value={item.value} key={index}>{item.name}</option>
))
}
</Form.Control>
{this.renderError(meta)}
</div>
)
}
for input field:
widgetName = event => {
this.setState({
widgetName: event.target.value
})
}
for dropdown:
typeOfChart = event => {
this.setState({
typeOfChart: event.target.value
})
}
Form submit function & api calls:
onSubmit = () => {
this.setState({
loader: true
})
let chartType = new ChartType({ type: this.state.typeOfChart });
let chartTitle = new ChartTitle({ text: this.state.chartTitle });
let chartSeries = new ChartSeries({ name: this.state.seriesName });
let data = new ChartOptions({ chart: chartType, title: chartTitle, series: [chartSeries] });
let widgetdata = new WidgetOptions({ name: this.state.widgetName, basicsettings: data });
widgetdata.basicsettings = JSON.stringify(widgetdata.basicsettings);
if (this.props.match.params.id) {
widgetdata.id = this.props.match.params.id;
this.widgetApiService.UpdateWidgetApiService(widgetdata).then(data => {
toast.success("Successfully updated");
this.setState({ loader: false })
}).catch(error => {
toast.error("Somethng went wrong. Try Again");
return error;
})
}
else {
this.widgetApiService.ManageWidgetApiService(widgetdata).then(data => {
toast.success("successfully completed ");
this.setState({ toWidget:true, loader: false })
}).catch(error => {
toast.error("Somethng went wrong. Try Again");
return error;
})
}
}
this is how my redux form looks
<Form onSubmit={this.props.handleSubmit(this.onSubmit)}>
<Form.Group controlId="formWidgetName">
<Form.Label>Widget Name:</Form.Label>
<Field name="widgetName" component={this.renderField} type="text" value={this.state.widgetName} onChange={this.widgetName} placeholder="Widget Name" />
</Form.Group>
<Form.Group controlId="formChartType">
<Form.Label>ChartType</Form.Label>
<Field as="select" name="ChartType" type="select" component={this.renderSelect} value={this.state.typeOfChart} onChange={this.typeOfChart}>
</Field>
</Form.Group>
<Form.Group>
<Button variant="primary" type="submit">
{this.props.match.params.id ? "Update" : "Submit"}
</Button>
</Form.Group>
<div>
{(this.state.loader) ? <Loader /> : null}
</div>
</Form>
export default reduxForm({
form: 'Managed Widget Form',
validate
})(ManageWidget);
this is entire component
const Loader = () => <div className="loader"><ReactBootstrap.Spinner animation="border" /></div>;
class ManageWidget extends Component {
constructor(props) {
super(props)
this.state = {
toWidget:false,
widgetName: '',
seriesName: '',
chartTitle: '',
typeOfChart: '',
// initialName: '',
loader: false,
dropdownData: [
{ name:"Choose any Chart", value:"choose" },
{ name:"Bar Chart", value:"bar" },
{ name:"Pie Chart", value:"pie" },
{ name:"Column Chart", value:"column" }
],
defaultVal: ''
}
this.widgetApiService = new WidgetApiService();
toast.configure();
}
componentDidMount() {
if (this.props.match.params.id) {
this.widgetApiService.getWidgets().then(data => {
const getEditObj = data.filter(widget => (widget.id == this.props.match.params.id))
// console.log(this.state.dropdownData[0].value)
if (getEditObj) {
getEditObj.map(data => (
this.props.initialize({
widgetName: data.name,
seriesName: data.basicsettings.series[0].name,
chartTitle: data.basicsettings.title.text,
typeOfChart: data.basicsettings.chart.type
})
))
this.setState({typeOfChart: getEditObj[0].basicsettings.chart.type});
this.setState({defaultVal: getEditObj[0].basicsettings.chart.type});
console.log("selected value for chart: " + this.state.typeOfChart)
}
})
}
}
mapStateToProps = (state, props) => ({
enableReinitialize: false,
keepDirtyOnReinitialize: true,
initialValues: props.defaultValues
})
renderError({ touched, error }) {
if (touched && error) {
return (
<div>
{error}
</div>
)
}
}
renderField = ({ input, placeholder, type, meta }) => {
return (
<div>
<Form.Control {...input} type={type} placeholder={placeholder} />
{this.renderError(meta)}
</div>
)
}
renderSelect = ({ input,select, placeholder, type, meta, defaultValue }) => {
return (
<div>
<Form.Control as="select" {...select} {...input} type={type} placeholder={placeholder} >
{/* <option>Choose any Chart</option>
<option value="bar">Bar Chart</option>
<option value="pie">Pie Chart</option>
<option value="column">Column Chart</option> */}
{
this.state.dropdownData.map((item,index) => (
<option value={item.value} key={index}>{item.name}</option>
))
}
</Form.Control>
{this.renderError(meta)}
</div>
)
}
widgetName = event => {
this.setState({
widgetName: event.target.value
})
}
seriesName = event => {
this.setState({
seriesName: event.target.value
})
}
chartTitle = event => {
this.setState({
chartTitle: event.target.value
})
}
typeOfChart = event => {
this.setState({
typeOfChart: event.target.value
})
}
onSubmit = () => {
this.setState({
loader: true
})
let chartType = new ChartType({ type: this.state.typeOfChart });
let chartTitle = new ChartTitle({ text: this.state.chartTitle });
let chartSeries = new ChartSeries({ name: this.state.seriesName });
let data = new ChartOptions({ chart: chartType, title: chartTitle, series: [chartSeries] });
let widgetdata = new WidgetOptions({ name: this.state.widgetName, basicsettings: data });
widgetdata.basicsettings = JSON.stringify(widgetdata.basicsettings);
if (this.props.match.params.id) {
widgetdata.id = this.props.match.params.id;
this.widgetApiService.UpdateWidgetApiService(widgetdata).then(data => {
toast.success("Successfully updated");
this.setState({ loader: false })
}).catch(error => {
toast.error("Somethng went wrong. Try Again");
return error;
})
}
else {
this.widgetApiService.ManageWidgetApiService(widgetdata).then(data => {
toast.success("successfully completed ");
this.setState({ toWidget:true, loader: false })
}).catch(error => {
toast.error("Somethng went wrong. Try Again");
return error;
})
}
}
render() {
if(this.state.toWidget){
return <Redirect to="/admin/WidgetList" />
}
return (
<>
<Form onSubmit={this.props.handleSubmit(this.onSubmit)}>
<Form.Group controlId="formWidgetName">
<Form.Label>Widget Name:</Form.Label>
<Field
name="widgetName"
component={this.renderField}
type="text"
value={this.state.widgetName}
onChange={this.widgetName}
placeholder="Widget Name"
/>
</Form.Group>
<Form.Group controlId="formChartType">
<Form.Label>ChartType</Form.Label>
<Field
as="select"
name="ChartType"
type="select"
component={this.renderSelect}
value={this.state.typeOfChart}
defaultValue={this.state.defaultValue}
onChange={this.typeOfChart}>
</Field>
</Form.Group>
<Form.Group controlId="formWidgetName">
<Form.Label>Series Name:</Form.Label>
<Field
name="seriesName"
component={this.renderField}
type="text"
value={this.state.seriesName}
onChange={this.seriesName}
placeholder="Series Name"
/>
</Form.Group>
<Form.Group controlId="formWidgetName">
<Form.Label>Chart Title:</Form.Label>
<Field
name="chartTitle"
component={this.renderField}
type="text"
value={this.state.chartTitle}
onChange={this.chartTitle}
placeholder="Chart Title"
/>
</Form.Group>
<Form.Group>
<Button variant="primary" type="submit">
{this.props.match.params.id ? "Update" : "Submit"}
</Button>
</Form.Group>
<div>
{(this.state.loader) ? <Loader /> : null}
</div>
</Form>
</>
);
}
}
const validate = (values) => {
const errors = {}
if (!values.widgetName) {
errors.widgetName = "Please enter the widget name"
}
if (!values.seriesName) {
errors.seriesName = "Please enter the series name"
}
if (!values.chartTitle) {
errors.chartTitle = "Please enter the chart title"
}
return errors
}
export default reduxForm({
form: 'Managed Widget Form',
validate,
})(ManageWidget);

React-select with Formik not updating select field but does everything else

React-Select with Formik is not loading the selected value in select componenet but I'm able to get values on form submission and validation also works with Yup
Here is a codesandbox demo for the same - https://codesandbox.io/s/wild-violet-fr9re
https://codesandbox.io/embed/wild-violet-fr9re?fontsize=14
import React, { Component } from "react";
import { Formik, Form, ErrorMessage } from "formik";
import * as Yup from "yup";
import Select from "react-select";
const debug = true;
class SelectForm extends Component {
constructor(props) {
super(props);
this.state = {
stateList: [],
stateCity: "",
selectedState: "",
citiesToLoad: []
};
}
handleState(opt) {
console.log(opt.value);
let citiesList = [];
Object.keys(this.state.stateCity).forEach(key => {
if (key === opt.value) {
this.state.stateCity[key].map((cityName, j) => {
citiesList.push(cityName);
});
}
});
this.setState({
selectedState: opt.value,
citiesToLoad: citiesList
});
}
handleMyCity(opt) {
console.log(opt.value);
}
componentDidMount() {
let stateLi = [];
fetch(`stateCity.json`)
.then(response => {
console.log(response);
return response.json();
})
.then(data => {
console.log(data);
for (let key in data) {
if (data.hasOwnProperty(key)) {
stateLi.push(key);
}
}
this.setState({ stateCity: data, stateList: stateLi });
})
.catch(err => {
console.log("Error Reading data " + err); // Do something for error here
});
}
render() {
const { selectedState, stateList, citiesToLoad } = this.state;
const newStateList = stateList.map(item => ({ label: item, value: item }));
const newCitiesToLoad = citiesToLoad.map(item => ({
label: item,
value: item
}));
return (
<div id="signupContainer" className="signinup-container">
<h3 className="mb-4"> Sign Up </h3>
<Formik
initialValues={{
state: selectedState,
city: ""
}}
validationSchema={Yup.object().shape({
state: Yup.string().required("Please select state."),
city: Yup.string().required("Please select city.")
})}
onSubmit={(values, { resetForm, setErrors, setSubmitting }) => {
setTimeout(() => {
console.log("Getting form values - ", values);
setSubmitting(false);
}, 500);
}}
enableReinitialize={true}
>
{props => {
const {
values,
touched,
dirty,
errors,
isSubmitting,
handleChange,
setFieldValue,
setFieldTouched
} = props;
return (
<Form id="signUpForm" className="signinupForm" noValidate>
<div className="form-group">
<label htmlFor="state" className="form-label">
State
</label>
<Select
name="state"
id="state"
onBlur={() => setFieldTouched("state", true)}
value={values.state}
onChange={(opt, e) => {
this.handleState(opt);
handleChange(e);
setFieldValue("state", opt.value);
}}
options={newStateList}
error={errors.state}
touched={touched.state}
/>
</div>
<div className="form-group">
<label htmlFor="city" className="form-label">
City
</label>
<Select
name="city"
id="city"
onBlur={() => setFieldTouched("city", true)}
value={values.city}
onChange={(opt, e) => {
this.handleMyCity(opt);
setFieldValue("city", opt.value);
}}
options={newCitiesToLoad}
/>
</div>
{isSubmitting ? (
<span className="loader-gif">
<img src={loading} alt="Loading..." />
</span>
) : null}
<button
type="submit"
className="btn btn-filled"
disabled={!dirty || isSubmitting}
>
Submit
</button>
{/*Submit */}
</Form>
);
}}
</Formik>
</div>
);
}
}
export default SelectForm;
Upon selecting any value from the selecet dropdown, my selected value should appear in select box
You are setting the field value on onchange of select setFieldValue("state", opt.value); so you don't need to set value for the <Select>:
<Select
name="state"
id="state"
onBlur={() => setFieldTouched("state", true)}
onChange={(opt, e) => {
this.handleState(opt);
handleChange(e);
setFieldValue("state", opt.value);
}}
options={newStateList}
error={errors.state}
touched={touched.state}
/>
change for the both <Select>
react-select accepts an object as a value so you need to pass an object of
let object = {
"label": "Andhra Pradesh",
"value": "Andhra Pradesh"
}
bypassing an object in value the selected value appears in the select box
Here is a codesandbox demo https://codesandbox.io/s/floral-fire-8txrt
so updated code is
import React, { Component } from "react";
import { Formik, Form, ErrorMessage } from "formik";
import * as Yup from "yup";
import Select from "react-select";
const debug = true;
class SelectForm extends Component {
constructor(props) {
super(props);
this.state = {
stateList: [],
stateCity: "",
selectedState: "",
citiesToLoad: []
};
}
handleState(opt) {
console.log(opt.value);
let citiesList = [];
Object.keys(this.state.stateCity).forEach(key => {
if (key === opt.value) {
this.state.stateCity[key].map((cityName, j) => {
citiesList.push(cityName);
});
}
});
this.setState({
selectedState: opt,
citiesToLoad: citiesList
});
}
handleMyCity(opt) {
console.log(opt.value);
}
componentDidMount() {
let stateLi = [];
fetch(`stateCity.json`)
.then(response => {
console.log(response);
return response.json();
})
.then(data => {
console.log(data);
for (let key in data) {
if (data.hasOwnProperty(key)) {
stateLi.push(key);
}
}
this.setState({ stateCity: data, stateList: stateLi });
})
.catch(err => {
console.log("Error Reading data " + err); // Do something for error here
});
}
render() {
const { selectedState, stateList, citiesToLoad } = this.state;
const newStateList = stateList.map(item => ({ label: item, value: item }));
const newCitiesToLoad = citiesToLoad.map(item => ({
label: item,
value: item
}));
return (
<div id="signupContainer" className="signinup-container">
<h3 className="mb-4"> Sign Up </h3>
<Formik
initialValues={{
state: selectedState,
city: ""
}}
validationSchema={Yup.object().shape({
state: Yup.string().required("Please select state."),
city: Yup.string().required("Please select city.")
})}
onSubmit={(values, { resetForm, setErrors, setSubmitting }) => {
setTimeout(() => {
console.log("Getting form values - ", values);
setSubmitting(false);
}, 500);
}}
enableReinitialize={true}
>
{props => {
const {
values,
touched,
dirty,
errors,
isSubmitting,
handleChange,
setFieldValue,
setFieldTouched
} = props;
return (
<Form id="signUpForm" className="signinupForm" noValidate>
<div className="form-group">
<label htmlFor="state" className="form-label">
State
</label>
<Select
name="state"
id="state"
onBlur={() => setFieldTouched("state", true)}
value={values.state}
onChange={(opt, e) => {
this.handleState(opt);
handleChange(e);
setFieldValue("state", opt);
}}
options={newStateList}
error={errors.state}
touched={touched.state}
/>
</div>
<div className="form-group">
<label htmlFor="city" className="form-label">
City
</label>
<Select
name="city"
id="city"
onBlur={() => setFieldTouched("city", true)}
value={values.city}
onChange={(opt, e) => {
this.handleMyCity(opt);
setFieldValue("city", opt);
}}
options={newCitiesToLoad}
/>
</div>
{isSubmitting ? (
<span className="loader-gif">
<img src={loading} alt="Loading..." />
</span>
) : null}
<button
type="submit"
className="btn btn-filled"
disabled={!dirty || isSubmitting}
>
Submit
</button>
{/*Submit */}
</Form>
);
}}
</Formik>
</div>
);
}
}
export default SelectForm;

Resources