3

我正在使用react-hook-form库来创建一个多步动态大表单(超过 70 个字段)。我的大部分表单字段都是动态的(用户可以通过单击 + 按钮添加/删除更多字段)。为了实现这一点,我使用了 react-hooks 并为每个动态字段添加了一个钩子。但我觉得我的代码不够抽象,可能有更聪明的方法来做到这一点。下面是我的表单的示例屏幕截图:

在此处输入图像描述

钩子和表单字段的相应代码在这里:

         // hooks
         const [sizes, setSizes] = React.useState([0]);
         const [formats, setFormats] = React.useState([0]);

         const [counter, setCounter] = React.useState(1);

         // buttons
         const addSizesFieldset = () => {
         setSizes(prevIndexes => [...prevIndexes, counter]);
         setCounter(prevCounter => prevCounter + 1);
         };

         const removeSizesFieldset = index => () => {
         setSizes(prevIndexes => [
         ...prevIndexes.filter(item => item !== index)
         ]);
         setCounter(prevCounter => prevCounter - 1);
         };


         const addFormatsFieldset = () => {
         setFormats(prevIndexes => [...prevIndexes, counter]);
         setCounter(prevCounter => prevCounter + 1);
         };

         const removeFormatsFieldset = index => () => {
         setFormats(prevIndexes => [
         ...prevIndexes.filter(item => item !== index)
         ]);
         setCounter(prevCounter => prevCounter - 1);
         };


        // form field arrays
        {sizes.map(index => {
            const fieldName = `sizes[${index}]`;
            return (
                <fieldset name={fieldName} key={fieldName} >
                    <h4>Sizes:</h4>
                    <label>
                        Size:
                        <input
                            type="text"
                            placeholder="Input"
                            name={`${fieldName}.size`}
                            ref={register}

                        />
                    </label>

                    <button type="button" onClick={addSizesFieldset}>
                        +
                    </button>
                    <button type="button" onClick={removeSizesFieldset(index)}>
                        -
                    </button>
                </fieldset>
            );
        })}
            {formats.map(index => {
                const fieldName = `formats[${index}]`;
                return (
                    <fieldset name={fieldName} key={fieldName} >
                        <h4>Formats:</h4>
                        <label>
                            Format:
                            <input
                                type="text"
                                placeholder="Input"
                                name={`${fieldName}.format`}
                                ref={register}

                            />
                        </label>

                        <button type="button" onClick={addFormatsFieldset}>
                            +
                        </button>
                        <button type="button" onClick={removeFormatsFieldset(index)}>
                            -
                        </button>
                    </fieldset>
                );
            })} ´´

想象一下,您正在const add/remove...Fieldset为 70 个字段执行这些钩子瞬间和函数。对我来说,这似乎太多重复了,但除了“React 可以处理多个钩子,所以不用担心”评论之外,我找不到任何解决方案或新想法。

如果您想在代码沙盒上尝试您的想法,我还准备了以下代码:

https://codesandbox.io/s/react-hook-form-wizard-form-yzno5

4

3 回答 3

3

也许如果你使用useReducer,如果你有多个状态会更好

示例

const initialState = {
  counter: 1,
  sizes: [0],
  formats: [0],
};

function reducer(state, action) {
  switch (action.type) {
    case 'add':
      return {
        ...state,
        counter: state.counter + 1,
        [action.typeObject]: [...state.sizes, state.counter + 1]
      };
    case 'remove':
      return {
        ...state,
        counter: state.counter - 1,
        [action.typeObject]: [...state.sizes.filter(item => item !== action.index)]
      };
    default:
      return state;
  }
}

/***/

const [stateReducer, dispatch] = React.useReducer(reducer, initialState);

/***/

const addSizesFieldset = () => {
  dispatch({ type: 'add', typeObject: 'sizes' })
};

const removeSizesFieldset = index => () => {
  dispatch({ type: 'remove', typeObject: 'sizes', index })
};

const addFormatsFieldset = () => {
 dispatch({ type: 'add', typeObject: 'formats' })
};

const removeFormatsFieldset = index => () => {
  dispatch({ type: 'remove', typeObject: 'formats', index })
};

测试一下

这是我通过分叉代码的简单实现: https ://codesandbox.io/s/react-hook-form-wizard-form-1h4yq


编辑:

您甚至可以通过在参数中添加“typeObject”来删除 2 个函数:

const addFieldset = field => () => {
  dispatch({ type: 'add', typeObject: field })
};

const removeFieldset = (index, field) => () => {
  dispatch({ type: 'remove', typeObject: field, index })
};

于 2020-01-28T16:41:25.883 回答
3

你考虑过使用我们新的自定义钩子useFieldArray吗?也许它是那些动态领域的替代解决方案。我还没有测试过动态附加,值得一试。欢迎反馈。

您也可以在我们的频谱频道中跟进问题:https ://spectrum.chat/react-hook-form?tab=posts

https://react-hook-form.com/api#useFieldArray

function App() {
  const { register, control, handleSubmit } = useForm({
    // defaultValues: {}; you can populate the fields by this attribute 
  });
  const { fields, append, prepend, remove } = useFieldArray({ control, name: "test" });

  return (
    <form onSubmit={handleSubmit(data => console.log("data", data))}>
      <ul>
        {fields.map((item, index) => (
          <li key={item.id}>
            {/* for empty validation register make sure pass empty object  */}
            <input name={`test[${index}].name`} ref={register({})} /> 
            <button onClick={() => remove(index)}>Delete</button>
          </li>
        )}
      </ul>
      <section>
        <button type="button" onClick={() => append({ name: "test" })} >
          append
        </button>
        <button type="button" onClick={() => prepend({ name: "test1" })}>
          prepend
        </button>
    </form>
  );
}
于 2020-01-30T04:15:38.777 回答
0

你可以制作 React 17 中引入的自定义钩子。

使用FieldCounter.js

import React, { useState } from "react";

const useFieldCounter = () => {
  const [fieldState, setFieldState] = useState([0]);
  const [counter, setCounter] = useState(1);

  const addField = () => {
    setFieldState((prevState) => [...prevState, counter]);
    setCounter((prevCounter) => prevCounter + 1);
  };

  const removeField = () => {
    if (counter > 1) {
      setFieldState((prevState) => [
        ...prevState.filter((item) => item !== counter - 1),
      ]);
      setCounter((prevCounter) => prevCounter - 1);
    }
  };
  return [fieldState, addField, removeField];
};

export default useFieldCounter;

在你的表单中使用这个钩子

const [sizes, addSizesFieldset, removeSizesFieldset] = useFieldCounter()
const [formats, addFormatsFieldset, removeFormatsFieldset] = useFieldCounter()
于 2021-10-26T11:23:00.803 回答