如何使用 React 获取表单提交中 dynamic/multiple 字段的值?

How to get values of dynamic/multiple fields on formsubmit with React?

我有一个表单,用户可以在其中添加文本字段以添加更多项目,但我很难在他们提交时将项目从表单中取出。

文本字段是由一个组件函数创建的(如下所示),整个表单不包含在一个函数中,正如我在许多示例中看​​到的那样,这意味着我不能简单地将值存储为陈述并稍后阅读(我认为..?)。

字段使用以下代码生成:

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 &&
                    <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);
}

然而,这可能不是一个好的方法。

我想要实现的是:

理想情况下没有任何第三方库,没有它们就可以找到解决方案。

一个可能的 'solution' 我想过有一个隐藏的输入元素,其状态包含数组,然后在表单提交上获取它的值,而不是每个单独的字段,但是这导致数组 'sent' 作为逗号分隔的列表,如果任何值中有逗号,这可能会导致问题。

或者,找到一种方法将每个输入字段的值传递给 handleSubmit(也许通过道具?)也可以,但我不太确定如何实现。

谢谢:)

可以添加redux store来传参如下;

    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 &&
                          <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>

您可以跟踪呈现表单的元素中的项目,换句话说,将使用状态移动到表单元素。将状态值和 setter 传递给子 texfield 元素并调用状态 setter 而不是让这些元素拥有自己的状态。

您应该能够在您的 onsubmit 中访问该状态

我最终通过在主表单组件内部而不是在每个单独的表单元素组件中管理状态来解决这个问题,然后将 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 访问。

有时 React 的行为很神秘。您可以尝试直接从 itemNames 数组添加映射值和名称。

const handleInputChange = (i, e) => {
    //const values = [...itemNames];
    //values[i] = e.target.value;
    itemNames[i] = e.target.value
    setItemNames([...itemNames]);
}

通过这种方式,您可以更新 itemNames 数组。