Add unique keys for items in reactjs - javascript

I have the next application:
https://codesandbox.io/s/suspicious-murdock-87j13?file=/SubForm.js
<Form.List name="names">
{(fields, { add, remove }) => {
return (
<div>
{fields.map((field, index) => {
console.log("m", field);
return (
<Form.Item
{...(index === 0
? formItemLayout
: formItemLayoutWithOutLabel)}
label={index === 0 ? "Passengers" : ""}
required={false}
key={index}
>
<Form.Item
{...field}
name={[field.name, "outer"]}
fieldKey={[field.fieldKey, "outer"]}
validateTrigger={["onChange", "onBlur"]}
rules={[
{
required: true,
whitespace: true,
message:
"Please input passenger's name or delete this field."
}
]}
noStyle
>
<Input
placeholder="passenger name"
style={{ width: "60%" }}
/>
</Form.Item>
<Form.Item
{...field}
name={[field.name, "outer1"]}
fieldKey={[field.fieldKey, "outer1"]}
validateTrigger={["onChange", "onBlur"]}
rules={[
{
required: true,
whitespace: true,
message:
"Please input passenger's name or delete this field."
}
]}
noStyle
>
<Input
placeholder="passenger name"
style={{ width: "60%" }}
/>
</Form.Item>
<Form.Item>
<InnerForm fieldKey={field.key} />
</Form.Item>
{fields.length > 1 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
style={{ margin: "0 8px" }}
onClick={() => {
remove(field.name);
}}
/>
) : null}
</Form.Item>
);
})}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "60%" }}
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
When user click on Add field button, in console appears the warning:
Warning: Encountered two children with the same key, `0`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
Also this warning appears when i click on Add sub-field. I can't figure out why this warning appears.
Question: Why the above warning appears and how to remove it?

You have two Form.Item Component in your code, and they use same key index, try to use different key like 'item1' + index and 'item2' + index.
And, in your index.js file also have same problem.
Here is Demo forked from you: https://codesandbox.io/s/dark-darkness-xz0wf?file=/SubForm.js

As reactJs primary works on re rendering items every time you make a change the state of that component. It requires an unique key to track each and every item so that re-rendering can be optimized. Not all items needs to be re-render every time.
This warning essentially means that you have multiple items with same key names. In order to fix this you have to provide unique "key" attribute to each child component which is getting updated, components which can potentially trigger an re-render.

you don't need to use multiple times key={index} or key={"g"}
use once only.
Please try following
import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Form, Input, Button, Space } from 'antd';
import { MinusCircleOutlined, PlusOutlined } from '#ant-design/icons';
const Demo = () => {
const onFinish = values => {
console.log('Received values of form:', values);
};
return (
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
return (
<div>
{fields.map(field => (
<Space key={field.key} style={{ display: 'flex', marginBottom: 8 }} align="start">
<Form.Item
{...field}
name={[field.name, 'innerName']}
fieldKey={[field.fieldKey, 'innerName']}
>
<Input placeholder="passenger 1" />
</Form.Item>
<Form.Item
{...field}
name={[field.name, 'innerName2']}
fieldKey={[field.fieldKey, 'innerName2']}
>
<Input placeholder="passenger 2" />
</Form.Item>
<MinusCircleOutlined
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
ReactDOM.render(<Demo />, document.getElementById('container'));

Related

In react, I`m using ant design library and inside "map" function using two <Row> tag, the key for the <Row> element is same and hence error

Warning message => react-dom.development.js:88 Warning: Encountered two children with the same key, 0. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off" >
<Form.List name="users" initialValue={[{ccn: "",cn:"",title:"" ,major: "",year:""}]}>
{(fields, { add, remove }) => (
<>
{fields.map(field => (
<Space key={field.key} style={{ display: 'flex', marginBottom: 8 }} align="baseline">
<div>
<Row>
<Form.Item
{...field}
name={[field.name, 'ccn']}
fieldKey={[field.fieldKey, 'ccn']}
rules={[{ required: true, message: 'Missing ccn' }]}
>
<AutoComplete
options={cocOptions}
style={{
width: 200,
}}
onSelect={oncocSelect}
onSearch={oncocSearch}
placeholder="Country of College/University"
/>
</Form.Item>
<Form.Item
{...field}
name={[field.name, 'cn']}
fieldKey={[field.fieldKey, 'cn']}
rules={[{ required: true, message: 'Missing cn name' }]}
>
<AutoComplete
options={cnOptions}
style={{
width: 200,
}}
onSelect={oncnSelect}
onSearch={oncnSearch}
placeholder="College/University Name"
/>
</Form.Item>
</Row>
<Row>
<Form.Item
{...field}
name={[field.name, 'title']}
fieldKey={[field.fieldKey, 'title']}
rules={[{ required: true, message: 'Missing title name' }]}
>
<Input placeholder="title Name" />
</Form.Item>
<Form.Item
{...field}
name={[field.name, 'major']}
fieldKey={[field.fieldKey, 'major']}
rules={[{ required: true, message: 'Missing major name' }]}
>
<AutoComplete
options={mOptions}
style={{
width: 200,
}}
onSelect={onmSelect}
onSearch={onmSearch}
placeholder="Major"
/>
</Form.Item>
<Form.Item
{...field}
name={[field.name, 'year']}
fieldKey={[field.fieldKey, 'year']}
rules={[{ required: true, message: 'Missing year name' }]}
>
<Select placeholder="Year" style={{ width: 120 }} onChange={handleChange}>
<Option value="jack">Jack</Option>
<Option value="lucy">Lucy</Option>
<Option value="Yiminghe">yiminghe</Option>
</Select>
</Form.Item>
<MinusCircleOutlined onClick={() => remove(field.name)} />
</Row>
</div>
</Space>
))}
<Form.Item>
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
Add field
</Button>
</Form.Item>
</>
)}
</Form.List>
<Form.Item hidden>
<Button ref={myButton} type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>

How to reduce the default margin/padding applied by ant design between elements in <Form>?

I am using ant design <Form layout="vertical"> and I want to set the space between Price Map elements inside <Form> to be less. I tried setting style={{margin:"0px"}} on elements but it has no change on UI.
Here's the code for the Price Map.
<Form.Item
label="Price Map"
rules={[{ required: true, message: "Please input Prices!" }]}
>
{selectedProducts.flatMap(productId =>
products
.filter(product => product.id === productId)
.map(product => (
<Input.Group compact layout="horizontal">
<Form.Item label={product.productName + " : "} />
{product.priceList.map(priceObj => (
<Input
defaultValue={priceObj.price}
addonBefore={priceObj.type}
rules={[
{
required: true,
message: "Please input price!"
}
]}
style={{ width: "34%" }}
/>
))}
</Input.Group>
))
)}
</Form.Item>
<Form.Item style={{ marginBottom: 0 }} />
I was able to achieve this by setting style={{ marginBottom: 5 }} for the <Input>
Here's my updated code.
<Form.Item
label="Price Map"
rules={[{ required: true, message: "Please input Prices!" }]}
>
{selectedProducts.flatMap(productId =>
products
.filter(product => product.id === productId)
.map(product => (
<Input.Group compact layout="horizontal">
<div style={{ marginRight: 5 }}> { product.productName + " : " }</div>
{product.priceList.map(priceObj => (
<Input
defaultValue={priceObj.price}
addonBefore={priceObj.type}
rules={[
{
required: true,
message: "Please input price!"
}
]}
onChange={(changedValue) => {
setPriceMap({
...priceMap,
[priceObj.priceId]: priceObj.price
});
}}
style={{ width: "34%" , marginBottom: 5 }}
/>
))}
</Input.Group>
))
)}
</Form.Item>

Save value in react js

I have a form with sub forms.
I have submit button for outer form but in the same time and for inner. The outer form works fine, it saves all data. but i can't get values from inner form clicking on
<Form.Item>
<Button type="primary" htmlType="submit">
Submit Inner
</Button>
</Form.Item>
Clicking on that i want to get values inside inner component in:
const onFinish = v => {
console.log("inner", v);
};
The subform code:
<Form.List onFinish={onFinish} name={[props.fieldKey, "inner"]}>
{(fields, { add, remove }) => {
return (
<div>
{fields.map((field, index) => (
<div>
<Form.Item
// name={"aar"}
{...field}
name={[field.name, "innerName"]}
fieldKey={[field.fieldKey, "innerName"]}
key={index}
noStyle
>
<Input
placeholder="passenger name"
style={{ width: "60%" }}
/>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit Inner
</Button>
</Form.Item>
</div>
))}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "60%" }}
>
<PlusOutlined /> Add field
</Button>
</div>
);
}}
</Form.List>
But when i click on this i get values in the outer form.
Question: How to get values from inner form clicking on Submit Inner button?
demo: https://codesandbox.io/s/wonderful-ives-o81ue?file=/SubForm.js
now i get:
expected result:

Set first fields open in a dynamic form

I have a dynamic form where users can add data. By default i want to set the form in this way:
How you can see, the first pair of input is open when user opens the application.
I made this by changing this:
const Demo = () => {
const onFinish = values => {
console.log("Received values of form:", values);
};
return (
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
console.log(fields);
return (
<div>
{fields.map(field => (
<Space
key={field.key}
style={{ display: "flex", marginBottom: 8 }}
align="start"
>
<Form.Item
{...field}
name={[field.name, "first"]}
fieldKey={[field.fieldKey, "first"]}
rules={[{ required: true, message: "Missing first name" }]}
>
<Input placeholder="First Name" />
</Form.Item>
<Form.Item
{...field}
name={[field.name, "last"]}
fieldKey={[field.fieldKey, "last"]}
rules={[{ required: true, message: "Missing last name" }]}
>
<Input placeholder="Last Name" />
</Form.Item>
<MinusCircleOutlined
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
to this:
const Demo = () => {
const onFinish = values => {
console.log("Received values of form:", values);
};
const firstDefaultOpen = {
name: 0,
key: 0,
isListField: true,
fieldKey: 0
};
return (
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
console.log(fields);
return (
<div>
{fields.concat(firstDefaultOpen).map(field => (
<Space
key={field.key}
style={{ display: "flex", marginBottom: 8 }}
align="start"
>
<Form.Item
{...field}
name={[field.name, "first"]}
fieldKey={[field.fieldKey, "first"]}
rules={[{ required: true, message: "Missing first name" }]}
>
<Input placeholder="First Name" />
</Form.Item>
<Form.Item
{...field}
name={[field.name, "last"]}
fieldKey={[field.fieldKey, "last"]}
rules={[{ required: true, message: "Missing last name" }]}
>
<Input placeholder="Last Name" />
</Form.Item>
<MinusCircleOutlined
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
Issue: But appears a problem when i try to add another input, because writing on first pair of inputs i can see the same text on the next inputs. Probably this is hapenening because of the same ids.
Question: How to set default open first pair of input as in the image using the code above?
demo: https://codesandbox.io/s/hungry-star-nu5ld?file=/index.js:235-2322
ps: i know that exists the next solution:
<Form initialValues={{ users: [""] }} name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
console.log(fields);
return (
<div>
{fields.map(field => (
...
...
But i don't want this solution because i can't interact with <Form/> tag in my real application, so even this could be a solution, but i need another, like mine with concat() but of course to be workable.

ReactJS and Material UI CheckBox State Change

Im using Material UI Checkbox to create a form that takes input and then in theory adds the new values or updates the values into a state object. This form is used for both editing a holiday or creating a new holiday.
I'm struggling with finding why my state isnt being updated upon clicking on a checkbox or typing in an input box. The checkbox wont change to checked/unchecked and the input wont remvoe the value when i use backspace to remove characters or enter new characters.
Dialog Box:
HolidayData:
{
"id": 1,
"ID": 1,
"HolidayName": "New Year's Day",
"HolidayDate": "05/20/2020",
"Branch": null,
"Hours": null,
"Web": true,
"Phone": true,
"CoOp": false,
"Active": true,
"Submitted": null,
"SubmittedBy": null,
"Published": "05/20/2020",
"PublishedBy": "John.Doe"
}
DialogBox Code:
const HolidayDialog = (props) => {
const [noticeModal, setNoticeModal] = React.useState(false);
const [selectedDate, setSelectedDate] = React.useState(new Date());
const [holidayData, setHolidayData] = React.useState(props.data);
useEffect(() => {
setHolidayData(props.data);
setNoticeModal(props.open)
});
const handleDateChange = (date) => {
setSelectedDate(date);
};
const handleClose = () => {
setNoticeModal(false);
};
const handleChange = (e) => {
const { name, checked } = e.target;
setHolidayData((prevState) => ({ ...prevState, [name]: checked }));
};
const updateValues = (e) => {
const { name, value } = e.target;
setHolidayData((prevState) => ({ ...prevState, [name]: value }));
};
return (
<Dialog
open={noticeModal}
TransitionComponent={Transition}
keepMounted
onClose={handleClose}
aria-labelledby="notice-modal-slide-title"
aria-describedby="notice-modal-slide-description"
>
<DialogTitle id="customized-dialog-title" onClose={handleClose}>
{holidayData.HolidayName ? holidayData.HolidayName : 'Create New Holiday'}
</DialogTitle>
<DialogContent dividers>
<form noValidate autoComplete="off">
<div className="row">
<div className="col">
<TextField required name="HolidayName" id="outlined-basic" label="Holiday Name" variant="outlined" onChange={updateValues} value={holidayData.HolidayName ? holidayData.HolidayName : ''}/>
</div>
<div className="col">
<TextField id="outlined-basic" label="Branch" variant="outlined" onChange={updateValues} value={holidayData.Branch ? holidayData.Branch : 'ALL'}/>
</div>
</div>
<div className="row mt-3">
<div className="col">
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<KeyboardDatePicker
disableToolbar
variant="inline"
format="MM/dd/yyyy"
margin="normal"
id="date-picker-inline"
label="Date picker inline"
value={selectedDate}
onChange={handleDateChange}
KeyboardButtonProps={{
'aria-label': 'change date',
}}
/>
</MuiPickersUtilsProvider>
</div>
<div className="col">
<TextField id="outlined-basic" label="Hours" variant="outlined" onChange={updateValues} value={holidayData.Hours ? holidayData.Hours : 'Closed'}/>
</div>
</div>
<div className="row mt-3">
<div className="col d-flex flex-column">
<FormControlLabel
control={
<Checkbox
checked={holidayData.Web ? holidayData.Web : false}
onChange={handleChange}
name="Web"
color="primary"
/>
}
label="Show on Web?"
/>
<FormControlLabel
control={
<Checkbox
checked={holidayData.CoOp ? holidayData.CoOp : false}
onChange={handleChange}
name="CoOp"
color="primary"
/>
}
label="CoOp Holiday?"
/>
</div>
<div className="col d-flex flex-column">
<FormControlLabel
control={
<Checkbox
checked={holidayData.Phone ? holidayData.Phone : false}
onChange={handleChange}
name="Phone"
color="primary"
/>
}
label="Use in IVR?"
/>
<FormControlLabel
control={
<Checkbox
checked={holidayData.Active ? holidayData.Active : false}
onChange={handleChange}
disabled
name="active"
color="primary"
/>
}
label="Active"
/>
</div>
</div>
</form>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleClose} color="default">
Cancel
</Button>
<Button autoFocus onClick={handleClose} color="primary">
Create Holiday
</Button>
</DialogActions>
</Dialog>
)
}
When i check a box or try to edit an input the handleChange and updateValues are firing. I believe it may be a syntax issue, but i cant seem to find anything. When console.log'ing event.target.name i get the correct name: Web for example
EDIT: The issue appears to be with the value and checked being equal to {holidayData.Phone ? holidayData.Phone : false} or the sorts. However if i bring the values down to {holidayData.Phone} itll start to throw errors:
A component is changing an uncontrolled input of type checkbox to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa).
It'll now allow me to check a checkbox but only once and throws this error over and over again and im not sure why or how to correct this?
Most probably the useEffect is causing the issue. You're resetting the data each time the component gets updated. You don't need that if you're already doing that with useState.
Check my example here with only the textbox: https://codesandbox.io/s/fancy-shape-14r63?file=/src/App.js
If you uncomment the useEffect, it stops working.

Categories