React js - Save previous value in Ant Design Steps - reactjs

I am using Ant Design Steps for my form but I am having a problem on how to save the value of the previous input.
Here is my code whole code:
import React from 'react';
import 'antd/dist/antd.css';
import './index.css';
import { Form, Icon, Input, Button, Steps } from 'antd';
const { Step } = Steps;
const steps = [
{
title: 'First',
content: 'First-content',
},
{
title: 'Second',
content: 'Second-content',
}
];
class NormalLoginForm extends React.Component {
constructor (props) {
super (props) ;
this.state = {
current: 0
}
}
next() {
const current = this.state.current + 1;
this.setState({ current });
}
prev() {
const current = this.state.current - 1;
this.setState({ current });
}
handleSubmit = e => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
};
render() {
const { current } = this.state
const { getFieldDecorator } = this.props.form;
return (
<div>
<Steps current={current}>
{steps.map(item => (
<Step key={item.title} title={item.title} />
))}
</Steps>
<Form onSubmit={this.handleSubmit} className="login-form">
{current === 0 ? (
<Form.Item>
{getFieldDecorator('username', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<Input
prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
placeholder="Username"
/>,
)}
</Form.Item>
): ''}
{current === 1 ? (
<Form.Item>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<Input
prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
type="password"
placeholder="Password"
/>,
)}
</Form.Item>
): ''}
</Form>
{current < steps.length - 1 && (
<Button type="primary" onClick={() => this.next()}>
Next
</Button>
)}
{current === steps.length - 1 && (
<Button type="primary" onClick={this.handleSubmit}>
Done
</Button>
)}
{current > 0 && (
<Button style={{ marginLeft: 8 }} onClick={() => this.prev()}>
Previous
</Button>
)}
</div>
);
}
}
What is happening is when I click next, the value of the previous input is gone. What I want to happen is when I click next the previous value will be save so when I click submit both of the values will be output in the console. The code comes from ant design and I used it for example

You should consider storing the data in a state :
constructor (props) {
super (props) ;
this.state = {
current: 0,
firstInput: '',
secondInput: '',
}
}
<Input
prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
placeholder="Username"
onChange={(e) => this.setState({firstInput: e.target.value})}
/>,
Then, in your submit press, you will be able to access the values of firstInput and secondInput by doing this.state.firstInput & this.state.secondInput.

Related

Why am i POST a lot of unwanted data?

I'm kind of new with java and ReactJS and I have a big issue where when I tried to post my data, it posted a lot of unwanted data like rendering the whole table.
My console.log() printed this :
location_dest_id: 2
location_id: 1
origin: "test3"
picking_type_id: 1
stock_move_ids: Array(1)
0:
altKey: false
bubbles: true
button: 0
buttons: 0
cancelable: true
clientX: 317
clientY: 652
ctrlKey: false
currentTarget: null
defaultPrevented: false
demand: "12"
detail: 1
done: "0"
eventPhase: 3
getModifierState: ƒ modifierStateGetter(keyArg)
isDefaultPrevented: ƒ functionThatReturnsFalse()
isPropagationStopped: ƒ functionThatReturnsFalse()
isTrusted: true
metaKey: false
movementX: 0
movementY: 0
nativeEvent: PointerEvent {isTrusted: true, pointerId: 1, width: 1,
height: 1, pressure: 0, …}
pageX: 317
pageY: 754
product_tmpl_id: 9
product_uom: "1"
relatedTarget: null
screenX: 317
screenY: 723
shiftKey: false
target: span
timeStamp: 203547.59999990463
type: "click"
view: Window {window: Window, self: Window, document: document, name: '', location: Location, …}
_reactName: "onClick"
_targetInst: null
From what I understand, the error is when I tried to pass "stock_move_ids" nested data, it POST so much data as like the console.log() above. What it should be passed is like this :
[
{
"date":"02-09-2022",
"origin":"test2",
"picking_type_id":2,
"location_id":1,
"location_dest_id":2,
"stock_move_ids":
[
{
"demand":12,
"done":0,
"product_uom":1,
"product_tmpl_id":18
}
]
}
]
Is there any way to solve my problem? My code is based on this template : https://codesandbox.io/s/j0opp
Here's my code looks like :
Parent
import React, { useEffect, useContext } from "react";
import { Button, Form, Input, DatePicker, Select } from 'antd';
import { Stockmovetable } from "./Stockmovetable";
import { AppContext } from '../../../context/Appcontext'
const Stockpickingnew = ({ title }) => {
const { Function, State } = useContext(AppContext)
const { fetchDataPickingType, fetchDataLocation, fetchDataPupuk, option, stock_move_ids, StockPick, StockPickFailed } = Function
const { dateFormat, dataPupuk, dataStockLocation, dataStockPickingType } = State
useEffect(() => {
fetchDataPickingType()
fetchDataLocation()
fetchDataPupuk()
}, [])
return (
<>
<div className='new'>
<div className="top">
<h1>{title}</h1>
</div>
<div className="bottom">
<div className="stockPicking">
<Form
name="stockPickings"
layout="vertical"
onFinish={StockPick}
onFinishFailed={StockPickFailed}
autoComplete="off"
>
<div className="left">
<Form.Item
label="Origin :"
name='origin'
>
<Input placeholder="Origin" />
</Form.Item>
<Form.Item
label="Picking Type :"
name='picking_type_id'
>
<Select
placeholder="Picking Type"
options={dataStockPickingType.map(e => ({label: e.name, value: e.id}))}
/>
</Form.Item>
<Form.Item
label="Date :"
name='date'
>
<DatePicker
format={dateFormat}
/>
</Form.Item>
</div>
<div className="right">
<Form.Item
label="Location :"
name='location_id'
>
<Select
placeholder="Tujuan Awal"
options={dataStockLocation.map(e => ({label: e.name, value: e.id}))}
/>
</Form.Item>
<Form.Item
label="Destination :"
name='location_dest_id'
>
<Select
placeholder="Tujuan Akhir"
options={dataStockLocation.map(e => ({label: e.name, value: e.id}))}
/>
</Form.Item>
</div>
<div className="stockMove">
<Form.List name="stock_move_ids">
{(stock_move_ids, { add, remove }) => {
return <Stockmovetable stock_move_ids={stock_move_ids} option={option} add={add} remove={remove} dataPupuk={dataPupuk} />;
}}
</Form.List>
</div>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
</div>
</div>
</div>
</>
)
}
export default Stockpickingnew
Child
import React from "react";
import { Form, Input, Button, Table, Select } from "antd";
import { PlusOutlined, MinusOutlined } from "#ant-design/icons";
const { Column } = Table;
export const Stockmovetable = props => {
const { stock_move_ids, add, remove, dataPupuk, option } = props;
return (
<Table
dataSource={stock_move_ids}
pagination={false}
footer={() => {
return (
<Form.Item>
<Button onClick={add}>
<PlusOutlined /> Add field
</Button>
</Form.Item>
);
}}
>
<Column
dataIndex={"product_tmpl_id"}
title={"Nama Produk"}
render={(value, row, index) => {
return (
<Form.Item name={[index, "product_tmpl_id"]}>
<Select
placeholder="Produk"
options={dataPupuk.map(e => ({ label: e.name, value: e.id }))}
/>
</Form.Item>
);
}}
/>
<Column
dataIndex={"demand"}
title={"Demand"}
render={(value, row, index) => {
// console.log(row);
return (
<Form.Item name={[index, "demand"]}>
<Input
placeholder="Demand"
/>
</Form.Item>
);
}}
/>
<Column
dataIndex={"done"}
title={"Done"}
render={(value, row, index) => {
return (
<Form.Item name={[index, "done"]}>
<Select
placeholder="Tujuan Akhir"
options={option}
/>
</Form.Item>
);
}}
/>
<Column
dataIndex={"product_uom"}
title={"product_uom"}
render={(value, row, index) => {
return (
<Form.Item name={[index, "product_uom"]}>
<Input
placeholder="product_uom"
/>
</Form.Item>
);
}}
/>
<Column
title={"Action"}
render={(value, row, index) => {
return (
<React.Fragment>
<Button
icon={<MinusOutlined />}
shape={"circle"}
onClick={() => remove(row.name)}
/>
</React.Fragment>
);
}}
/>
</Table>
);
};
Context
export const AppContext = createContext()
export const AppProvider = props => {
const Navigate = useNavigate()
const dateFormat = ['DD-MM-YYYY'];
const StockPick = (values) => {
console.log('Success:', values);
let stockpick = [{
date: moment(values.date).format("DD-MM-YYYY"),
origin: values.origin,
picking_type_id: values.picking_type_id,
location_id: parseInt(values.location_id),
location_dest_id: parseInt(values.location_dest_id),
stock_move_ids: [
{
demand: parseInt(values?.stock_move_ids?.[0]?.demand),
done: parseInt(values?.stock_move_ids?.[0]?.done),
product_uom: parseInt(values?.stock_move_ids?.[0]?.product_uom),
product_tmpl_id: values?.stock_move_ids?.[0]?.product_tmpl_id,
},
],
}];
let params = JSON.stringify(stockpick)
console.log(params)
axios.post('http://127.0.0.1:5000/api/stockpickings', params, { headers })
.then(() => {
Navigate('/')
})
.catch(error => {
if (error.response) {
console.log(error.response);
}
});
};
}
I think this is all I can provide / needed to fix my codings, if there's anything I need to add and or fix, please tell me. Thank you.
Okay after some hours tries
as the comment above, i think all i need to do is just not making the body as dataindex.
from :
<Form.List name="stock_move_ids">
{(stock_move_ids, { add, remove }) => {
return <Stockmovetable stock_move_ids={stock_move_ids} option={option} add={add} remove={remove} dataPupuk={dataPupuk} />;
}}
</Form.List>
to
<Form.List name="stock_move_ids">
{(stock_move, { add, remove }) => {
return <Stockmovetable stock_move_ids={stock_move_ids} option={option} add={add} remove={remove} dataPupuk={dataPupuk} />;
}}
</Form.List>
Thank you very much for the helps!

I want to handle error state in Form.Item Ant design. how to handle disable button?

I spend almost 1 week to solve this problem.
I want to disable the button while the field gets an error.
Somehow always the form.getFieldsError() gets Array(0), even the error message this is required or should be 7 digit was showed up.
If you have any idea please help me.
this is my code.
import React from 'react';
import { Form, Input, Button } from 'antd';
import { MinusCircleOutlined, PlusOutlined } from '#ant-design/icons';
const DynamicFieldSet = () => {
const MAX = 3;
const [form] = Form.useForm();
const onValuesChange = (changedValues, allValues) => {
console.log(allValues.lines);
};
console.log(
// this is always true
form.getFieldsError().filter(({ errors }) => errors.length).length === 0
);
return (
<Form
initialValues={{ lines: [''] }}
onValuesChange={onValuesChange}
form={form}
name={'lines'}
>
<Form.List name={'lines'}>
{(fields, { add, remove }, { errors }) => {
return (
<div>
{fields.map((field, index) =>
index < MAX ? (
<Form.Item required={false} key={field.key}>
<Form.Item
{...field}
validateTrigger={['onChange', 'onBlur']}
rules={[
{
required: true,
message: 'this is required',
},
{
min: 7,
max: 7,
message: 'should be 7 digit',
},
]}
noStyle
>
<Input
placeholder=""
style={{ width: '50%' }}
type="number"
/>
</Form.Item>
{index === 0 ? (
<>
<PlusOutlined
onClick={() => {
add();
}}
/>
</>
) : index === MAX - 1 ? (
<MinusCircleOutlined onClick={() => remove(field.name)} />
) : (
<>
<PlusOutlined
onClick={() => {
add();
}}
/>
<MinusCircleOutlined
onClick={() => remove(field.name)}
/>
</>
)}
</Form.Item>
) : null
)}
</div>
);
}}
</Form.List>
<Form.Item>
<Button disabled={false} htmlType="submit">
Send
</Button>
</Form.Item>
</Form>
);
};
export default DynamicFieldSet;
I'm not really sure why but form.getFieldsError() is just not going to work. In order to achieve what you need, you should try using form.validateFields() instead.
The validateFields method, returns a promise that shows all the fields' data or errors on them:
form.validateFields()
.then(values => {
// Do something with values
})
.catch(errors => {
// Do something with errors;
});
Note that this will validate all the fields that you haven't even touched, causing all the provided error messages of all the fields to be shown. So, in case you want to validate just one field or so, all you need to do is to provide an array of field's names like ['myInput'].
From here, just create a new state to be set when errors appear and use it to show/hide-enable/disable your button.

Was Form.create() from antd replaced?

i am trying to create a login page in react using antd. I found a tutorial for doing this,but i belive that it is outdated, because it gives an error. I read on a forum that form.create() is not available anymore, but i don't know how to replace it.Here is my code:
import { Form, Icon, Input, Button, Spin } from 'antd';
import { connect } from 'react-redux';
import { NavLink } from 'react-router-dom';
import * as actions from '../store/actions/auth';
const FormItem = Form.Item;
const antIcon = <Icon type="loading" style={{ fontSize: 24 }} spin />;
class NormalLoginForm extends React.Component {
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
this.props.onAuth(values.userName, values.password);
this.props.history.push('/');
}
});
}
render() {
let errorMessage = null;
if (this.props.error) {
errorMessage = (
<p>{this.props.error.message}</p>
);
}
const { getFieldDecorator } = this.props.form;
return (
<div>
{errorMessage}
{
this.props.loading ?
<Spin indicator={antIcon} />
:
<Form onSubmit={this.handleSubmit} className="login-form">
<FormItem>
{getFieldDecorator('userName', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
)}
</FormItem>
<FormItem>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
)}
</FormItem>
<FormItem>
<Button type="primary" htmlType="submit" style={{marginRight: '10px'}}>
Login
</Button>
Or
<NavLink
style={{marginRight: '10px'}}
to='/signup/'> signup
</NavLink>
</FormItem>
</Form>
}
</div>
);
}
}
const WrappedNormalLoginForm = Form.create()(NormalLoginForm);
const mapStateToProps = (state) => {
return {
loading: state.loading,
error: state.error
}
}
const mapDispatchToProps = dispatch => {
return {
onAuth: (username, password) => dispatch(actions.authLogin(username, password))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(WrappedNormalLoginForm);
When i try to run it, i get an error that says:
TypeError: _antd.Form.create(...) is not a function
How can i rewrite the code in order to make it run?
AntD has removed Form.create in v4 and you can replace the above code with the modified API structure like
class NormalLoginForm extends React.Component {
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
this.props.onAuth(values.userName, values.password);
this.props.history.push('/');
}
});
}
render() {
let errorMessage = null;
if (this.props.error) {
errorMessage = (
<p>{this.props.error.message}</p>
);
}
const { getFieldDecorator } = this.props.form;
return (
<div>
{errorMessage}
{
this.props.loading ?
<Spin indicator={antIcon} />
:
<Form onSubmit={this.handleSubmit} className="login-form">
<FormItem name='userName' rules={[{ required: true, message: 'Please input your username!' }]}>
<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
</FormItem>
<FormItem name="password" rules={[{ required: true, message: 'Please input your Password!' }]}>
<Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
</FormItem>
<FormItem>
<Button type="primary" htmlType="submit" style={{marginRight: '10px'}}>
Login
</Button>
Or
<NavLink
style={{marginRight: '10px'}}
to='/signup/'> signup
</NavLink>
</FormItem>
</Form>
}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
loading: state.loading,
error: state.error
}
}
const mapDispatchToProps = dispatch => {
return {
onAuth: (username, password) => dispatch(actions.authLogin(username, password))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(NormalLoginForm);
Please see the migration guidelines for more details
I think you are using Antd v4.*. Form.create({}) is no more in latest version, so you have to your code as follow to make it working. V4 has. major changes so you have to go through migration guidelines
import { Form, Input, Button, Spin } from 'antd';
import { LoadingOutlined, UserOutlined, LockOutlined } from "#ant-design/icons";
import { connect } from 'react-redux';
import { NavLink } from 'react-router-dom';
import * as actions from '../store/actions/auth';
const FormItem = Form.Item;
const antIcon = <LoadingOutlined style={{ fontSize: 24 }} />;
class NormalLoginForm extends React.Component {
handleFinish = (values) => {
this.props.onAuth(values.userName, values.password);
this.props.history.push('/');
}
render() {
let errorMessage = null;
if (this.props.error) {
errorMessage = (
<p>{this.props.error.message}</p>
);
}
return (
<div>
{errorMessage}
{
this.props.loading ?
<Spin indicator={antIcon} />
:
<Form onFinish={this.handleFinish} className="login-form">
<FormItem name="userName" rules={[{ required: true, message: 'Please input your username!' }]}>
<Input prefix={<UserOutlined style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
)}
</FormItem name="password" rules={[{ required: true, message: 'Please input your Password!' }]}>
<FormItem>
<Input prefix={<LockOutlined style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
)}
</FormItem>
<FormItem>
<Button type="primary" htmlType="submit" style={{marginRight: '10px'}}>
Login
</Button>
Or
<NavLink
style={{marginRight: '10px'}}
to='/signup/'> signup
</NavLink>
</FormItem>
</Form>
}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
loading: state.loading,
error: state.error
}
}
const mapDispatchToProps = dispatch => {
return {
onAuth: (username, password) => dispatch(actions.authLogin(username, password))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(NormalLoginForm);

Disable form.item 1 if form.item 2 is filled in

I want to hide form.item 1 if form.item 2 is filled in and the other way around.
I want to do it for the following code but i cant find the solution to this.
<Form.Item label="Url">
{getFieldDecorator('url')(<Input/>)}
</Form.Item>
<Form.Item label="Standaard Urls" >
{getFieldDecorator('url_id', {})(this.getSelectUrls())}
</Form.Item>
Basically i want too know how i can hide 1 form.item and the rest i can do myself
You need to use onFieldsChange of Form.create, that keeps your Form uncontrollable which is the cleanest way.
Wrap your Form with a state, pass it and use onFieldsChange like so:
const FormContainer = () => {
const [isVisible, setIsVisible] = useState(true);
const onFieldsChange = (_, changedFiels) => {
const { password } = changedFiels;
if (password) {
console.log(`Now changing ${password.name}`);
setIsVisible(false);
}
};
const ValidatedFields = Form.create({ onFieldsChange })(MyForm);
return <ValidatedFields isVisible={isVisible} />;
};
Warning: You need to figure how you want to "remove" the form field, it depends on if you need its state or not, for this you can use display CSS property:
// Will unmount the item and lose it state
{ isVisible && <Form.Item>...}
// Will keep the state and hide the item
<Form.Item style={{ display: isVisible ? 'auto' : 'none' }}>...
Full code:
In this example, you can write in the password field and it will hide other forms items:
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import { Input, Form, Icon } from 'antd';
import 'antd/dist/antd.css';
import './index.css';
const FormContainer = () => {
const [isVisible, setIsVisible] = useState(true);
const onFieldsChange = (_, changedFiels) => {
const { password } = changedFiels;
if (password) {
console.log(`Now changing ${password.name}`);
setIsVisible(false);
}
};
const ValidatedFields = Form.create({ onFieldsChange })(MyForm);
return <ValidatedFields isVisible={isVisible} />;
};
const MyForm = ({ form, isVisible }) => {
const { getFieldDecorator, validateFields } = form;
const handleSubmit = e => {
e.preventDefault();
validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
};
return (
<Form onSubmit={handleSubmit} className="login-form">
{isVisible && (
<Form.Item>
{getFieldDecorator('username', {
rules: [{ required: true, message: 'Please input your username!' }]
})(
<Input
prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
placeholder="Item 1"
/>
)}
</Form.Item>
)}
<Form.Item style={{ display: isVisible ? 'auto' : 'none' }}>
{getFieldDecorator('username2', {
rules: [{ required: true, message: 'Please input your username!' }]
})(
<Input
prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
placeholder="Item 2"
/>
)}
</Form.Item>
<Form.Item>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }]
})(
<Input
prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
type="password"
placeholder="Type here to hide other forms"
/>
)}
</Form.Item>
</Form>
);
};
ReactDOM.render(<FormContainer />, document.getElementById('root'));
const [showItem1, setShowItem1] = useState(true);
const [showItem2, setShowItem2] = useState(true);
onKeyPress = {() => {setShowItem1(true); setShowItem1(false); }}
onKeyPress = {() => {setShowItem1(false); setShowItem1(true); }}
{
showItem1 &&
<Form.Item label="Url">
{getFieldDecorator('url')(<Input/>)}
</Form.Item>
}
{
showItem2 &&
<Form.Item label="Standaard Urls" >
{getFieldDecorator('url_id', {})(this.getSelectUrls())}
</Form.Item>
}

React antd steps re-render child component to parent state change

I have long registration form which I have divided into parts using steps component of AntD.
// Defined Child Form Components
steps = {
basicInfo: {
title: 'Basic Info',
content: (
<BasicInfoForm
form={this.props.form}
user={this.user}
/>
),
},
addresses: {
title: 'Addresses',
content: (
<Address
form={this.props.form}
addresses={this.user.addresses}
/>
),
},
contactInfo: {
title: 'Contact Info',
content: (
<PhoneForm
form={this.props.form}
contactInfo={this.user.contactInfo}
/>
),
},
}
// Form configuring these child form components
<Form onSubmit={handleSubmit} className="add-edit-user-form">
<Fragment>
<Steps progressDot current={this.state.currentStep}>
{Object.keys(steps).map((key, index) => {
return <Step title={steps && steps[index] ? steps[index].title : null} key={index} />;
})}
</Steps>
<div className={styles['steps-content']}>{steps && steps[currentStep] ? steps[currentStep].content : null}</div>
<div className="steps-action">
{currentStep > 0 && (
<Button style={{ marginLeft: 8 }} onClick={() => this.prev()}>Previous</Button>
)}
{currentStep < steps.length - 1 && (
<Button type="primary" htmlType="submit">Next</Button>
)}
{currentStep === steps.length - 1 && (
<Button type="primary" htmlType="submit">Done</Button>
)}
</div>
</Fragment>
</Form>
// class variable to user object coming from parent.
user = this.props.user ? this.props.user : undefined;
// Child component
interface IAddressFormProps {
form: WrappedFormUtils;
addresses: IAddress[];
fieldName: string;
}
class Address extends Component<IAddressFormProps> {
totalAddresses: any = this.props.addresses && this.props.addresses ?
this.props.addresses : ([{}] as IAddress[]);
state = {
totalAddresses: this.totalAddresses ? this.totalAddresses : ([] as IAddress[]),
};
addAddress = () => {
this.setState((prevState: any) => ({
totalAddresses: [...prevState.totalAddresses, { lineOne: '', lineTwo: '', lineThree: '', city: '', state: '', zip: '', type: '' }],
}));
};
removeAddress = (index: number) => {
this.setState({
totalAddresses: this.state.totalAddresses.filter((item: IAddress, addressIndex: number) => index !== addressIndex),
});
};
render() {
const { getFieldDecorator } = this.props.form;
const fieldName = this.props.fieldName;
return (
<Fragment>
{this.state.totalAddresses.map((item: IAddress, index: number) => {
return (
<Row key={'container-' + index} type="flex" justify="start" gutter={16}>
<Col span={6}>
<Form.Item label="Type" key={'type-' + index}>
{getFieldDecorator(`${fieldName}[${index}].type`, {
initialValue: item.type,
rules: [{ required: true, message: 'Please input address type!' }],
})(
<Select placeholder="Type">
<Option value="Mailing">Mailing</Option>
<Option value="Business">Business</Option>
<Option value="Home">Home</Option>
<Option value="Other">Other</Option>
</Select>
)}
</Form.Item>
<Form.Item label="Line One" key={'lineOne-' + index}>
{getFieldDecorator(`${fieldName}[${index}].lineOne`, {
initialValue: item.lineOne,
rules: [{ required: true, message: 'Please input line one!' }],
})(<Input placeholder="Line One" />)}
</Form.Item>
<Form.Item label="Line Two" key={'lineTwo-' + index}>
{getFieldDecorator(`${fieldName}[${index}].lineTwo`, {
initialValue: item.lineTwo,
rules: [{ required: false, message: 'Please input line two!' }],
})(<Input placeholder="Line Two" />)}
</Form.Item>
<Form.Item label="Line Three" key={'lineThree-' + index}>
{getFieldDecorator(`${fieldName}[${index}].lineThree`, {
initialValue: item.lineThree,
rules: [{ required: false, message: 'Please input line three!' }],
})(<Input placeholder="Line Three" />)}
</Form.Item>
</Col>
<Col span={9}>
<Form.Item label="City" key={'city-' + index}>
{getFieldDecorator(`${fieldName}[${index}].city`, {
initialValue: item.city,
rules: [{ required: true, message: 'Please input city!' }],
})(<Input placeholder="City" />)}
</Form.Item>
<Form.Item label="State" key={'state-' + index}>
{getFieldDecorator(`${fieldName}[${index}].state`, {
initialValue: item.state,
rules: [{ required: true, message: 'Please input state!' }],
})(<Input placeholder="State" />)}
</Form.Item>
<Form.Item label="Zip" key={'zip-' + index}>
{getFieldDecorator(`${fieldName}[${index}].zip`, {
initialValue: item.zip,
rules: [{ required: true, message: 'Please input zip!' }],
})(<Input placeholder="Zip" />)}
</Form.Item>
</Col>
<Col span={4}>
<Button onClick={() => this.removeAddress(index)}>Remove</Button>
</Col>
</Row>
);
})}
<Button onClick={() => this.addAddress()}>Add address</Button>
</Fragment>
);
}
}
I want to maintain the state of user object throughout the steps means going back and forth.
On form submit, updating the user object.
next = (addEditUser: MutationFn<any, any>) => {
const form = this.props.form;
form.validateFields(async (err: any, values: any) => {
if (err) {
return false;
}
values.id = this.userId;
let variables = this.parentId ? { user: values, parentId: this.parentId } : { user: values };
const result = await addEditUser({ variables: variables });
if (result && result.data) {
if (this.state.currentStep === this.state.steps.length - 1) {
this.props.history.push('/user');
} else {
this.user = Object.assign(this.user, values);
const currentStep = this.state.currentStep + 1;
this.setState({ currentStep });
}
}
});
};
The user object is correctly updated but the child components are not. Why?
Thanks in advance.
In order to pass props to the state you have to use the getDerivedStateFromProps method.
In your child class you can add the following - This is an example for your Address Component where you receive the address from the parent Component via props
static getDerivedStateFromProps(props, state) {
const { address } = props;
if(address !== state.address) return { address }
}
Now here what happens is that if the parent Component sends new props the function checks if the prop.address is different from the state.address - this is the child Component state - and if it's different it's sets it to the value received from the props
Add this method in your Address class and it should work

Resources