How to get multiple checkbox values on submit reactjs - reactjs

I have a multiple recepient email and multiple checkbox column
I want to get each recepient email and checkbox values on submit.I am getting recepient emails on submit but no checkbox values. Kindly help
The form looks like this.
Here is my code
export default function ShareReportView(props) {
const [recipientEmails, updateRecicpientEmails] = useState({});
const handleInputChange = (e, name) => {
updateRecicpientEmails((prevState) => ({
...prevState,
[name]: e.target.value,
}));
};
const extratEmailList = (emailsList) => {
if (!emailsList || !Object.keys(emailsList).length) {
return;
}
console.log('obj email list',Object.values(emailsList))
return Object.values(emailsList);
};
const handlepermission = () => {
};
function sendEmail(recipientEmailsList) {
const rEmails = extratEmailList(recipientEmailsList);
console.log(rEmails);#prints all emails here
#here i want to get all checkbox values here on submit
}
return (
<div className="container">
{[...Array(count)].map((val, index) => (
<div key={index} className={`${styles["textField"]}`}>
<div style={{ float: "left" }}>
<Box
component="form"
sx={{
"& > :not(style)": { marginRight: 4, width: "31ch" },
}}
noValidate
autoComplete="off"
>
{" "}
<FormControl variant="standard">
<InputLabel
htmlFor="component-simple">
Recipient E mail
</InputLabel>
<Input
id="component-simple"
onChange={(event) =>
handleInputChange(
event,
`recipient_email_${index++}`,
false
)
}
name={`recipient_email_${index++}`}
key={`recipient_email_${index++}`}
disableUnderline={true}
/>
</FormControl>
<FormControlLabel
control={
<Checkbox
color="default"
onClick={() => {
handlepermission(`${index++}`);
}}
/>
}
label="Allow user to perfrom action"
name={`allow_user_edit_${index++}`}
/>
</Box>
</div>
</div>
))}
<div className="btn">
<button
className={`${styles.send}`}
onClick={() => sendEmail(recipientEmails)}
>
SEND
</button>
</div>
</div>
)}

I am not on my computer but following should work
export default function ShareReportView(props) {
const [recipientEmails, updateRecicpientEmails] = useState([]);
const handleEmailChange = (e, index) => {
let temp = [...recipientEmails]
let tempObj = {...temp[index]}
tempObj.email = e.target.value
temp.splice(index, 1, tempObj)
updateRecicpientEmails(temp)
};
const handlePermissionChange = (e, index) => {
let temp = [...recipientEmails]
let tempObj = {...temp[index]}
tempObj.permission = e.target.value
temp.splice(index, 1, tempObj)
updateRecicpientEmails(temp)
};
function sendEmail(recipientEmailsList) {
recipientEmails.forEach(e => {
console.log(e.email, e.permission)
})
}
return (
<div className="container">
{[...Array(count)].map((val, index) => (
<div key={index} className={`${styles["textField"]}`}>
<div style={{ float: "left" }}>
<Box
component="form"
sx={{
"& > :not(style)": { marginRight: 4, width: "31ch" },
}}
noValidate
autoComplete="off"
>
{" "}
<FormControl variant="standard">
<InputLabel
htmlFor="component-simple">
Recipient E mail
</InputLabel>
<Input
id="component-simple"
onChange={(event) =>
handleEmailChange(
event,
index
)
}
name={`recipient_email_${index++}`}
key={`recipient_email_${index++}`}
disableUnderline={true}
/>
</FormControl>
<FormControlLabel
control={
<Checkbox
color="default"
onClick={(e) => {
handlePermissionChange(e, index);
}}
/>
}
label="Allow user to perfrom action"
name={`allow_user_edit_${index++}`}
/>
</Box>
</div>
</div>
))}
<div className="btn">
<button
className={`${styles.send}`}
onClick={() => sendEmail(recipientEmails)}
>
SEND
</button>
</div>
</div>
)}
Let me know if you feel any issues, will be happy to help you, you should also change the logic of add and remove entries button. On add button just add a new object with empty values in recipientEmails list. and use your map function in render on recipientEmails.
Edit # 1
function addNewEntry(){ //call this on add new entry button
let temp = [...recipientEmails]
temp.push({
email: '',
permission: false
})
updateRecicpientEmails(temp)
}
you can use addNewEntry for adding new row. but now your will have to edit your render function something like this
replace {[...Array(count)].map((val, index) => (
with {recipientEmails.map((val, index) => (
in your return staement

You need to save multiple values on the same object per recipient, I did this change on your handleInputChange function, now it creates an object per recipient
const handleInputChange = (e, name) => {
updateRecicpientEmails((prevState) => ({
...prevState,
[name]: {
email: e.target.value
}
}));
};
and I call it like this
handleInputChange(event, `recipient_${index}`, false)
removed _email from there.
And for the handle permission, just add a new property to the recipient object with the checkbox value
const handlepermission = (index, value) => {
updateRecicpientEmails((currentRecipients) => ({
...currentRecipients,
[index]: {
...currentRecipients[index],
allow: value
}
}));
};
this function runs on input change, so just add this to prop to the input:
onChange={({ target: { checked } }) => {
handlepermission(`recipient_${index}`, checked);
}}
To be honest is easier if you use the native form submit handler and FormData API, here is an example:
https://codesandbox.io/s/formdata-api-example-xkvi8

Related

Can't filter data on users select option and render it onclick

I am making a form web application in react.js with select options(using material UI) and an onclick button. The problem is that I don't know how to filter the data from my local API so that when the user chooses an option on the UI they will then have that specific data from the API rendered when they press submit.
Currently, I have mapped all the data to the submit button:
Here is the data from the API:
If for example the user just selects Entry Point and clicks submit then I just want all of the data for Entry Point to be shown.
This is how I am rendering the data in react:
const initialData = ({
first: "",
second: "",
third: "",
fourth: ""
});
const Form = () =>{
const [Data, updateData] = React.useState(initialData);
const [users, setUsers] = useState([]);
const handleChange = (e) => {
updateData({
...Data,
[e.target.name]: e.target.value
});
};
const handleAutocompletefirstChange = (e, value) => {
updateData({
...Data,
first: value,
});
};
const handleAutocompletesecondChange = (e, value) => {
updateData({
...Data,
second: value
});
};
const handleAutocompletethirdChange = (e, value) => {
updateData({
...Data,
third: value
});
};
const handleAutocompletefourthChange = (e, value) => {
updateData({
...Data,
fourth: value
});
};
const options = ['Entry Point', 'Exit Point', 'MTOW (TONNES)', '5 to 40', 'More than 40 and upto 120', 'More than 120 and upto 250', 'More than 250 and upto 350', 'More than 350 and upto 450', 'More than 450']
const fetchData = async (e) => {
setSpinner(true);
e.preventDefault();
const res = await fetch('http://localhost:3000/getdata_withQuery');
console.log(res);
const data = await res.json();
setUsers(data);
console.log(Data);
const newData = await fetch('/api', {
method: 'POST',
headers: {
'content-type' : 'application/json',
'Accept' : 'application/json'
},
body: JSON.stringify({
name: Data.initialData
})
})
.then(res => res.json(setSpinner(false)));
console.log(newData);
}
return (
<div className='container'>
<Content>
<div className='features'>
<div className='buttons'>
<h1>Fill in Details</h1>
<form>
<div className='ComboBox'>
<div style={{marginLeft:'0%', marginTop: '0px'}}>
<Autocomplete
name='initialData'
onChange={(event, value) => handleAutocompletefirstChange(event, value)}
options={options}
style={{ width: 300 }}
renderInput={(params) =>
<TextField {...params} label="select" variant="outlined" name="first" onChange={handleChange} />}
/>
</div>
<br/>
<div style={{marginLeft:'0%', marginTop: '0px'}}>
<Autocomplete
name='initialData'
onChange={(event, value) => handleAutocompletesecondChange(event, value)}
options={options}
style={{ width: 300 }}
renderInput={(params) =>
<TextField {...params} label="select" variant="outlined" name="second" onChange={handleChange}/>}
/>
</div>
<br/>
<div style={{marginLeft:'0%', marginTop: '0px'}}>
<Autocomplete
name='initialData'
onChange={(event, value) => handleAutocompletethirdChange(event, value)}
options={options}
style={{ width: 300 }}
renderInput={(params) =>
<TextField {...params} label="select" variant="outlined" name="third" onChange={handleChange}/>}
/>
</div>
<br/>
<div style={{marginLeft:'0%', marginTop: '0px'}}>
<Autocomplete
name='initialData'
onChange={(event, value) => handleAutocompletefourthChange(event, value)}
options={options}
style={{ width: 300 }}
renderInput={(params) =>
<TextField {...params} label="select" variant="outlined" name="fourth" onChange={handleChange}/>}
/>
</div>
<br/>
</div>
<div className='btn'>
<Button type="submit" variant="primary" onClick={(e) => fetchData(e)}>Submit</Button>{' '}
<Button type='clear' variant="danger">Clear</Button>{' '}
</div>
</form>
<div className='data'>
{users.map((item) => (
<div className='map_Data'>
<ol key={item.ENTRY_POINT1}>
Entry_Point: { item.ENTRY_POINT1 }
<br />
Exit_Point: { item.EXIT_POINT1 }
<br />
Route_Point: { item.RoutePoint }
<br />
Distance_Nm: { item.DistanceNm }
<br />
MTOW (TONNES): { item.Distance_km }
<br />
fourty: {item.fourty}
<br/>
onehundredtwenty: {item.onehundredtwenty}
<br/>
twohundredfifty: {item.twohundredfifty}
<br/>
threehundredfifty: {item.threehundredfifty}
<br/>
fourhundredfifty: {item.fourhundredfifty}
<br/>
morefourhundredfifty: {item.morefourhundredfifty}
</ol>
</div>
))}
</div>
</div>
</div>
</div>
)
}
In my Node API I made this query to pull the data from my MSSQL database:
async function getdata_withQuery() {
try {
let pool = await sql.connect(config);
let res = await pool.request().query("select [ENTRY_POINT1], [EXIT_POINT1], [RoutePoint], [DistanceNm], ROUND([Distance_km],0) AS [Distance_km], ROUND([Distance_km] * 0.44, 0) AS [fourty], ROUND([Distance_km] * 0.58, 0) AS [onehundredtwenty], ROUND([Distance_km] * 0.88, 0) AS [twohundredfifty], ROUND([Distance_km] * 1, 0) AS [threehundredfifty], ROUND([Distance_km] * 1.14, 0) AS [fourhundredfifty], ROUND([Distance_km] * 1.30, 0) AS [morefourhundredfifty] from [dbo].['Data File$']");
return res.recordsets;
} catch (error) {
console.log(" mathus-error :" + error);
}
}
For more information here is the repo for my form web-app (src/components/ComboBox.js):
https://github.com/N00rAhmed/Avtag-Form/tree/working-branch
Also here is my Node.js Api: https://github.com/N00rAhmed/flytag-api
I have tried using the filter method in react.js and I was able to make a search filter using a text field, but not using select options because I kept on coming across errors which i didn't understand. I also tried doing an onclick handler on the select option but I quickly realised that wouldn't work.
you just get the data once, then give the array to jsx based on your search
this is a function that returns an array based on the given array and the proprety that you want to filter with
export const findWithProp = (array, prop) => {
var arrayToReturn =[];
if (prop=== "ALL") {return array;}
else {
array.forEach((element) => {
if (element[prop]){arrayToReturn.push(element);}
});
}
return arrayToReturn;};
so in your useEffect each time the value of the filter change let's call it "newValue"
const[newValue,setNewValue] = useState("");
you can update the arrayToShow and give it to jsx
const[arrayToShow,setArrayToShow] = useState([]);
useEffect(()=>{
setArrayToShow(findWithProp (users,newValue))
},[newValue])
in your jsx :
{
arrayToShow.map(element=>{
return(
// your logic
)
}

Submitting a form React - MUI v5, delivers a wrong value from one field

I have a react from made with MUI (I'm using MUI v5))
Everything works ok, until I hit submit.
The submitted values are ok except for the select, which is delayed, I mean, suppose I select the first value, and the default value is 0, then hitting submit will say that the selected value is 0, then, if I hit submit once more, immediately, then it says the correct answer, see, it’s kind of delayed by one render of the component.
I wanted to have all values in one object state, but I had to separate some of them, like a date value (uploadDate) and the select value (category value)
This is the simplified code:
const AddVideoAndCategories = ({
addVideo,
getAllCategoriesWithoutVideos,
categories,
categoriesLoaded,
resetDrawerMenuItems,
addDrawerItem,
setCurrentDrawerMenuItems
}) => {
const [ formValues, setFormValues ] = useState(defaultValues);
const [ uploadDate, setUploadDate ] = React.useState(today());
const [ selectedCategory, setSelectedCategory ] = useState('');
const { handleSubmit, reset, control } = useForm();
const navigate = useNavigate();
useEffect(() => {
if (!categoriesLoaded) {
/// Load the categories from the back end to redux
}
}, []);
const handleCategoryChange = (e) => {
setSelectedCategory(e.target.value);
};
const handleInputChange = (e) => {
let { name, value } = e.target;
setFormValues({
...formValues,
[name]: value
});
};
async function handleCtrlClick(target) {
/// allow pasting the clipboard with just ctrl click
}
const handleClick = (event) => {
// In that case, event.ctrlKey does the trick.
if (event.ctrlKey) {
event.stopPropagation();
handleCtrlClick(event.target);
}
};
const myHandleSubmit = () => {
let arr = selectedCategory.split('^'); /// This trick allowed me to show the selected category name instead of the categoryId
setFormValues({
...formValues,
uploadDate: uploadDate,
categoryId: arr[0]
});
alert(JSON.stringify(formValues, null, 4));
};
const handleReset = () => {
/// resets all fields
};
return (
<form onSubmit={handleSubmit}>
<Box
component="form"
sx={{
'& > :not(style)': { m: 1, width: '30ch', marginTop: '50px', marginBottom: '0' }
}}
noValidate
autoComplete="off"
>
<Stack spacing={3}>
<TextField
…
/>
<TextField
…
/>
<TextField
…
/>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
id="author-uploadDate"
name="uploadDate"
label="Video UploadDate"
value={uploadDate}
defaultValues={uploadDate}
onChange={(newValue) => {
setUploadDate(newValue);
}}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
<TextField
…
/>
<FormControl sx={{ minWidth: 120, maxWidth: '500px', width: '270px' }}>
<InputLabel id="video categories">Video Category</InputLabel>
<Select
labelId="video categories"
id="simple-select"
value={selectedCategory}
label="Video Category"
onChange={handleCategoryChange}
>
{categories.map((category) => {
return (
<MenuItem value={category.id.toString() + '^' + category.name}>
{category.name}
</MenuItem>
);
})}
</Select>
</FormControl>
<TextField
…
/>
</Stack>
<Button variant="contained" endIcon={<SendIcon />} size="medium" onClick={myHandleSubmit}>
Submit
</Button>
<Button variant="outlined" startIcon={<DeleteIcon />} size="medium" onClick={handleReset}>
Reset
</Button>
</Box>
</form>
);
};
// title, origin, author, uploadDate, url, excerpt
const defaultValues = {
title: '',
origin: 'Vimeo',
author: '',
uploadDate: today(),
categoryId: 0,
url: '',
excerpt: ''
};
function mapStateToProps(state) {
…
}
function mapDispatchToProps(dispatch) {
…
}
export default connect(mapStateToProps, mapDispatchToProps)(AddVideoAndCategories);
Can somebody see why that happens?

input tag loses focus after one character in react-querybuilder

I am rendering a customized react-querybuilder. Whenever I add a rule the input box is rendered with default empty value. The problem is that when I enter one character in the Input box it loses focus.
This does seem like a duplicate question. But, after trying out the solutions mentioned below -
Storing value in state.
autoFocus on input tag (this is messed it up even further!)
I am not able to figure it out.
I have added the code to stackblitz
Please find the relevant code:
const [queryOutput, setQueryOutput] = useState("");
...
<QueryBuilder
{...props}
controlElements={{
combinatorSelector: props => {
let customProps = {
...props,
value: props.rules.find(x => x.combinator) ? "or" : props.value
};
return (
<div className="combinator-wrapper">
<button className="form-control-sm btn btn-light mt-2">
{customProps.value.toUpperCase()}
</button>
</div>
);
},
addRuleAction: props => {
return (
<button
className={props.className}
title={props.title}
onClick={e => {
return props.handleOnClick(e);
}}
>
+ Add New Rule
</button>
);
},
addGroupAction: props => {
return (
<button
className={props.className}
title={props.title}
onClick={e => {
return props.handleOnClick(e);
}}
>
{props.label}
</button>
);
},
valueEditor: ({
className,
field,
operator,
inputType,
value,
handleOnChange,
level
}) => {
if (field === "enabled") {
return (
<input
className={className}
type="checkbox"
checked={value !== "" ? value : false}
onChange={e => handleOnChange(e.target.checked)}
/>
);
}
return (
<input
className={className}
value={value}
onChange={e => handleOnChange(e.target.value)}
/>
);
}
}}
onQueryChange={query => {
let customQuery = { ...query, combinator: "or" };
return setQueryOutput(
formatQuery(customQuery ? customQuery : query, "sql")
);
}}
/>
Needed to assign a reference of the valueEditor component rather than defining it inline(so that it does not create a new instance on every render).
Updated the relevant code:
const valueEditor = ({
className,
field,
operator,
inputType,
value,
handleOnChange,
level
}) => (
<input
className={className}
value={value}
onChange={e => handleOnChange(e.target.value)}
/>
);
.....
<QueryBuilder
{...props}
controlElements={{
...
valueEditor
...
}}
/>

Material-UI Autocomplete onChange not updates value

I want to use onChange event on Autocomplete component to get current selected values.
The problem is that it does not working as expected, so when I click to check/uncheck value checkbox is still unchecked but in console i can see that new value was added
uncoment this part to make it works:
value={myTempVal}
onChange={(event, newValue) => {
setMyTempVal(newValue);
console.log(newValue);
}}
online demo:
https://codesandbox.io/embed/hardcore-snowflake-7chnc?fontsize=14&hidenavigation=1&theme=dark
code:
const [myTempVal, setMyTempVal] = React.useState([]);
<Autocomplete
open
multiple
value={myTempVal}
onChange={(event, newValue) => {
setMyTempVal(newValue);
console.log(newValue);
}}
disableCloseOnSelect
disablePortal
renderTags={() => null}
noOptionsText="No labels"
renderOption={(option, { selected }) => {
return (
<>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.title}
</>
);
}}
options={option2}
// groupBy={option => option.groupName}
getOptionLabel={option => option.title}
renderInput={params => (
<div>
<div>
<SearchIcon />
</div>
<TextField
variant="outlined"
fullWidth
ref={params.InputProps.ref}
inputProps={params.inputProps}
/>
</div>
)}
/>
You need to get donors receivers and options variables out of the function. Those variables get re-created at each render, this means that their reference changes at each render, and as Autocomplete makes a reference equality check to decide if an option is selected he never finds the options selected.
const donors = [...new Set(data.map(row => row.donor))].map(row => {
return {
groupName: "Donors",
type: "donor",
title: row || "null"
};
});
const receivers = [...new Set(data.map(row => row.receiver))].map(row => {
return {
groupName: "Receivers",
type: "receiver",
title: row || "null"
};
});
const option2 = [...donors, ...receivers];
export const App = props => {
const [myTempVal, setMyTempVal] = React.useState([]);
return (
<Autocomplete
open
multiple
...
You can also add getOptionSelected to overwrite the reference check :
<Autocomplete
open
multiple
disableCloseOnSelect
disablePortal
renderTags={() => null}
noOptionsText="No labels"
getOptionSelected={(option, value) => option.title === value.title}
renderOption={(option, { selected }) => {
return (
<>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.title}
</>
);
}}
options={option2}
// groupBy={option => option.groupName}
getOptionLabel={option => option.title}
renderInput={params => (
<div>
<div>
<SearchIcon />
</div>
<TextField
variant="outlined"
fullWidth
ref={params.InputProps.ref}
inputProps={params.inputProps}
/>
</div>
)}
/>
This can help:
Replace
checked={selected}
To
checked={myTempVal.filter(obj=>obj.title===option.title).length!==0}
The complete solution
import React from "react";
import "./styles.css";
import TextField from "#material-ui/core/TextField";
import Autocomplete from "#material-ui/lab/Autocomplete";
import CheckBoxOutlineBlankIcon from "#material-ui/icons/CheckBoxOutlineBlank";
import CheckBoxIcon from "#material-ui/icons/CheckBox";
import Checkbox from "#material-ui/core/Checkbox";
import SearchIcon from "#material-ui/icons/Search";
const icon = <CheckBoxOutlineBlankIcon fontSize="small" />;
const checkedIcon = <CheckBoxIcon fontSize="small" />;
const data = [
{ donor: "Trader Joe's", receiver: "Person-to-Person" },
{ donor: "Trader Joe's", receiver: "Homes with Hope" },
{ donor: "Santa Maria", receiver: "Gillespie Center" },
{ donor: "Santa Maria", receiver: null }
];
export const App = props => {
const donors = [...new Set(data.map(row => row.donor))].map(row => {
return {
groupName: "Donors",
type: "donor",
title: row || "null"
};
});
const receivers = [...new Set(data.map(row => row.receiver))].map(row => {
return {
groupName: "Receivers",
type: "receiver",
title: row || "null"
};
});
const option2 = [...donors, ...receivers];
const [myTempVal, setMyTempVal] = React.useState([]);
return (
<Autocomplete
open
multiple
value={myTempVal}
disableCloseOnSelect
disablePortal
renderTags={() => null}
noOptionsText="No labels"
renderOption={(option, { selected }) => {
return (
<>
<Checkbox
onClick={
()=>{
if(myTempVal.filter(obj=>obj.title===option.title).length!==0){
setMyTempVal([...myTempVal.filter(obj=>obj.title!==option.title)],console.log(myTempVal))
}else{
setMyTempVal([...myTempVal.filter(obj=>obj.title!==option.title),option],console.log(myTempVal))
}
}
}
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={myTempVal.filter(obj=>obj.title===option.title).length!==0}
/>
{option.title}
</>
);
}}
options={option2}
// groupBy={option => option.groupName}
getOptionLabel={option => option.title}
renderInput={params => (
<div>
<div>
<SearchIcon />
</div>
<TextField
variant="outlined"
fullWidth
ref={params.InputProps.ref}
inputProps={params.inputProps}
/>
</div>
)}
/>
);
};
export default App;
It is bit late to Answer this question but it might help someone.
In your code you have added onChange event in Autocomplete. When you click on checkbox it will trigger 2 times, one for checkbox and one for Autocomplte. Hence 2nd time trigger makes again checkbox unchecked so u get value in console but still checkbox is empty.
You can remove your checkbox in renderOption and use checked and uncheked icon instaed of checkbox.
renderOption={(option, { selected }) => {
return (
<React.Fragment>
{selected ? <CheckedIcon> : <uncheckedIcon>}
<div>
{option.title}
</div>
</React.Fragment>
</>
);
}}

How to access field data in other field

I have a modal form with a form that uses Formik. Here are two pictures that show two states of that form that can be toggled with a switch.Initially I fill text into fields which can be added dynamically and stored as an array with .
The second picture shows how I toggled to textarea. There you can also add text with commas that will be turned into an array.
Is there any way to fill data in input fields from the first screen, toggle into textarea and access already inputted data.
I understand formik keeps that state somewhere. But at the moment these fields have a separate state.
Here is my component:
class ModalForm extends React.Component {
constructor(props) {
super(props);
this.state = {
disabled: true,
};
}
onChange = () => {
this.setState({
disabled: !this.state.disabled,
});
};
render() {
var {
visible = false,
onCancel,
onRequest,
submitting,
setSubscriberType,
editing,
subscriptionTypeString,
tested,
selectedGates,
} = this.props;
const { gateId } = selectedGates.length && selectedGates[0];
const handleSubmit = values => {
console.log(values);
onRequest && onRequest({ gateId, ...values });
};
const { disabled } = this.state;
return (
<Modal
footer={null}
closable
title="Список абонентов для выбранного гейта"
visible={visible}
onCancel={onCancel}
onOk={handleSubmit}
destroyOnClose
width="600px"
>
<StyledDescription>
<Switch onChange={this.onChange} />
<StyledLabel>массовый ввод</StyledLabel>
</StyledDescription>
<Formik
initialValues={{ abonents: [''] }}
onSubmit={handleSubmit}
render={({ values, handleChange }) => (
<Form>
{disabled ? (
<FieldArray
name="abonents"
render={arrayHelpers => {
return (
<div>
{values.abonents.map((value, index) => (
<div key={index}>
<MyTextInput
placeholder="Абонент ID"
name={`abonents.${index}`}
value={value}
onChange={handleChange}
/>
<Button
shape="circle"
icon="delete"
onClick={() => {
arrayHelpers.remove(index);
}}
/>
</div>
))}
<Button type="dashed" onClick={() => arrayHelpers.push('')}>
<Icon type="plus" />Добавить абонента
</Button>
</div>
);
}}
/>
) : (
<StyledField
placeholder="Введите ID абонентов через запятую"
name="message"
component="textarea"
/>
)}
<Footer>
<Button type="primary" htmlType="submit">
Запросить
</Button>
</Footer>
</Form>
)}
/>
</Modal>
);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.0.0/umd/react-dom.production.min.js"></script>
Pretty easy, formik stores values inside values.abonents, hence you can use it inside textarea
let { Formik, Form, Field, ErrorMessage, FieldArray } = window.Formik;
function App () {
const [disabled, setDisabled] = React.useState(false) // some boilerplate code
function submit (values) {
console.log('submit', values)
}
return (
<Formik
initialValues={{ abonents: [] }}
onSubmit={submit}
render={({ values, handleChange, setFieldValue }) => (
<Form>
<FieldArray
name='abonents'
render={arrayHelpers => {
if (!disabled) {
return (
<textarea onChange={(e) => {
e.preventDefault()
setFieldValue('abonents', e.target.value.split(', '))
}} value={values.abonents.join(', ')}></textarea>
)
}
return (
<div>
{
values.abonents.map((value, index) => (
<div key={index}>
<input
placeholder='Абонент ID'
name={`abonents.${index}`}
value={value}
onChange={handleChange}
/>
<button onClick={(e) => {
e.preventDefault()
arrayHelpers.remove(index)
}}>
-
</button>
</div>
))
}
<button onClick={(e) => {
e.preventDefault()
arrayHelpers.push('')
}}>
+
</button>
</div>
)
}}
/>
<button type='submit'>Submit</button>
<button onClick={e => {
e.preventDefault()
setDisabled(!disabled)
}}>toggle</button>
</Form>
)}
/>
)
}
ReactDOM.render(<App />, document.querySelector('#root'))
<script src="https://unpkg.com/react#16.9.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/formik/dist/formik.umd.production.js"></script>
<div id='root'></div>
I found a solution. You have got to give the same name to the input and textarea, so whe you add text in input will automatically change text in textarea
<FieldArray
name="abonents"
render={arrayHelpers => {
and
<StyledField
placeholder="Введите ID абонентов через запятую"
name="abonents"
component="textarea"
/>
These two fields have same name so they are rendered interchangeably but have common text inside them

Resources