#javascript #reactjs #forms #material-ui #jsx
Вопрос:
У меня есть форма, в которой пользователь может добавлять текстовые поля, чтобы добавлять больше элементов, и я изо всех сил пытаюсь извлечь элементы из формы, когда они ее отправляют.
Текстовые поля создаются функцией компонента (как показано ниже), и вся форма не содержится в одной функции, как я видел во многих примерах, что означает, что я не могу просто сохранить значения в виде состояния и прочитать их позже (я думаю..?).
Поля генерируются с помощью следующего кода:
const itemNameTextFields = () => {
const [itemNames, setItemNames] = React.useState(['']);
const addItemNameField = () => setItemNames([...itemNames, '']);
const handleInputChange = (i, e) => {
const values = [...itemNames];
values[i] = e.target.value;
setItemNames(values);
}
return (
itemNames.map((itemName, i) => (
<Box component='span' key={`item_${i}`} sx={{display: 'flex', flexWrap: 'wrap'}}>
<TextField
value={itemName}
onChange={e => handleInputChange(i, e)}
label={`Item ${i 1}`}
name={`item_${i}`}
className={classes.itemName}
/>
{itemNames.length-1 === i amp;amp;
<Button
endIcon={<Icon icon={plusCircleOutline} />}
onClick={addItemNameField}
>
<small>Add another</small>
</Button>
}
</Box>
));
);
}
И мой код для обработки отправляемой формы выглядит примерно так:
const handleSubmit = e => {
e.preventDefault();
let obj = {
/* ... */
name : e.target['name'].value || '',
items : [] //this is what I am missing
};
console.log(obj);
}
Однако это может быть не самый лучший способ сделать это.
Чего я хотел бы достичь, так это либо:
- для
items
того, чтобы внутриobj
был массив, содержащий значения каждого текстового поля из первого блока кода. Например, obj = {элементы: [‘item1’, ‘item2’,/ …/]} или - для
obj
того, чтобы содержать каждый элемент по отдельности. Например, obj = {item_1: ‘item1’, item_2: ‘item’2, /…/}
В идеале без каких-либо сторонних библиотек, где решение можно было бы найти и без них.
Одним из возможных «решений», о котором я думал, было бы наличие скрытого элемента ввода с состоянием, содержащим массив, а затем получение значения в formsubmit, а не в каждом отдельном поле, но это приводит к тому, что массив «отправляется» в виде списка, разделенного запятыми, что может вызвать проблемы, если в каком-либо из значений есть запятые.
Альтернативно, найти способ передать значение каждого поля ввода handleSubmit
(возможно, через реквизиты?) это тоже могло бы сработать, но я не совсем уверен, как этого добиться.
Спасибо 🙂
Ответ №1:
Вы можете добавить хранилище redux для передачи параметров следующим образом;
const React = window.React;
const ReactDOM = window.ReactDOM;
const {Box, Button, TextField} = window.MaterialUI;
const Redux = window.Redux;
function itemReducer(state = [], action) {
switch (action.type) {
case 'SET':
let tmp = [...state];
tmp[action.index] = action.item;
return state = tmp;
default:
return state;
}
}
const store = Redux.createStore(itemReducer, [])
const handleSubmit = e => {
e.preventDefault()
let obj = {
name : e.target.innerHTML || '',
items : store.getState()
}
console.log(obj)
}
const App = () => {
const itemNameTextFields = () => {
const [itemNames, setItemNames] = React.useState([''])
const addItemNameField = () => {
setItemNames([...itemNames, '']);
}
const handleInputChange = (i, e) => {
store.dispatch({
type:"SET",
index:i,
item:e.target.value
});
const values = [...itemNames]
values[i] = e.target.value
setItemNames(values)
}
return (
itemNames.map((itemName, i) => (
<Box component='span' key={"item_" i} sx={{display: 'flex', flexWrap: 'wrap', marginBottom:1}}>
<TextField
value={itemName}
onChange={e => handleInputChange(i, e)}
label={"Item " i}
name={"item" i}
variant="outlined"
fullWidth
/>
{itemNames.length-1 === i amp;amp;
<Button
size='small'
onClick={addItemNameField}
disabled={!itemNames[itemNames.length-1].length > 0}
style={{marginLeft:4}}
>
<small>Add another</small>
</Button>
}
</Box>
))
)
}
return itemNameTextFields();
}
ReactDOM.render(
<div><App/><Button onClick={handleSubmit}>Submit</Button></div>,
document.getElementById('root')
);
<script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/redux@4.1.1/dist/redux.js"></script>
<script src="https://unpkg.com/@material-ui/core@latest/umd/material-ui.development.js"></script>
<div id="root"></div>
Комментарии:
1. Привет, не могли бы вы, пожалуйста, объяснить свой ответ? Является ли Redux строго необходимым? Я не использовал его ни для чего другого.
2. Это действительно зависит от архитектуры вашего приложения. Redux-это просто шаблон для хранения ваших данных между вашими компонентами. Но вы также можете привязать его к своему пользовательскому интерфейсу. Проверить jsfiddle.net/1092z4ck для вашего дела.
Ответ №2:
Вы можете отслеживать элементы в элементе, отображающем форму, другими словами, перемещать состояние использования в элемент формы. Передайте значение состояния и задатчик дочерним элементам текстового поля и вызовите задатчик состояния вместо того, чтобы разрешать этим элементам иметь свое собственное состояние.
Вы должны быть в состоянии получить доступ к этому состоянию в своем onsubmit
Ответ №3:
Иногда реакция ведет себя таинственным образом. Вы можете попробовать напрямую добавить значение и имя сопоставления из itemNames
массива.
const handleInputChange = (i, e) => {
//const values = [...itemNames];
//values[i] = e.target.value;
itemNames[i] = e.target.value
setItemNames([...itemNames]);
}
Таким образом, вы можете обновить массив имен товаров.
Ответ №4:
В итоге я решил эту проблему, управляя состоянием внутри основного компонента формы, а не в каждом отдельном компоненте элемента формы, а затем передавая функции useState каждому компоненту как таковому:
const FormElement = ({value, onChange)} => {
const handleChange = e => {
onChange(e.target.value)
}
return (
<TextField
{...props}
onChange={handleChange}
value={value}
/>
)
}
const AddNewEventForm = () => {
const classes = useStyles()
const [formElementValue, setFormElementValue] = React.useState('')
const handleSubmit = e => {
e.preventDefault()
/* ... */
console.log(formElementValue)
}
return (
<form onSubmit={handleSubmit}>
<Box>
<FormElement value={formElementValue} onChange={setFormElementValue} />
</Box>
<Box>
<Button type="submit">
Submit
</Button>
</Box>
</form>
)
}
Обрабатывая состояние таким образом, данные из дочерних компонентов могут быть доступны handleSubmit
внутри родительской функции.