Как получить значения динамических/нескольких полей в formsubmit с помощью React?

#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 внутри родительской функции.