【问题标题】:Save Form values in ReactJS using checkboxes使用复选框在 ReactJS 中保存表单值
【发布时间】:2023-01-29 23:33:38
【问题描述】:

我使用 React Hook 表单创建了一个表单组件。该组件由一组复选框和一个文本输入组成。当用户单击最后一个复选框 custom 时出现文本输入。这个想法是:当用户点击它时会出现一个文本输入,用户可以添加自定义答案/选项。例如:如果用户在输入中键入 test,那么当用户保存表单时,test 值应该出现在数组中,但 custom 文本不应出现在数组中。在我的应用程序中,我无权访问 const onSubmit = (data) => console.log(data, "submit");,因此我需要更改 Component 组件中的值。现在,当我单击提交时,我在最终数组中得到了 custom 值。
问题:如何解决上述问题?

const ITEMS = [
  { id: "one", value: 1 },
  { id: "two", value: 2 },
  { id: "Custom Value", value: "custom" }
];

export default function App() {
  const name = "group";
  const methods = useForm();
  const onSubmit = (data) => console.log(data, "submit");

  return (
    <div className="App">
      <FormProvider {...methods}>
        <form onSubmit={methods.handleSubmit(onSubmit)}>
          <Component ITEMS={ITEMS} name={name} />
          <input type="submit" />
        </form>
      </FormProvider>
    </div>
  );
}
export const Component = ({ name, ITEMS }) => {
  const { control, getValues } = useFormContext();
  const [state, setState] = useState(false);

  const handleCheck = (val) => {
    const { [name]: ids } = getValues();

    const response = ids?.includes(val)
      ? ids?.filter((id) => id !== val)
      : [...(ids ?? []), val];

    return response;
  };

  return (
    <Controller
      name={name}
      control={control}
      render={({ field, formState }) => {
        return (
          <>
            {ITEMS.map((item, index) => {
              return (
                <>
                  <label>
                    {item.id}
                    <input
                      type="checkbox"
                      name={`${name}[${index}]`}
                      onChange={(e) => {
                        field.onChange(handleCheck(e.target.value));
                        if (index === ITEMS.length - 1) {
                          setState(e.target.checked);
                        }
                      }}
                      value={item.value}
                    />
                  </label>
                  {state && index === ITEMS.length - 1 && (
                    <input
                      {...control.register(`${name}[${index}]`)}
                      type="text"
                    />
                  )}
                </>
              );
            })}
          </>
        );
      }}
    />
  );
};


演示:https://codesandbox.io/s/winter-brook-sml0ww?file=/src/Component.js:151-1600

【问题讨论】:

  • 看起来你已经开始工作了。我使用该链接验证了提交用户在自定义字段中键入的文本是记录到控制台的内容。也许我不明白你的问题。一旦字段可见,您是否试图阻止看到“自定义”文本?
  • @codejockie,试试这个:选择所有复选框并在输入中添加文本,提交后,然后取消选择一个复选框并提交,您会看到值没有正确保存。你找到问题了吗?
  • @codejockie,你能帮忙吗?
  • 我稍微修改了你的代码。例子请看以下链接:codesandbox.io/s/cocky-aryabhata-7jprlr?file=/src/Custom.js
  • @codejockie,如何获取值数组?示例:[first, second, inputValue]

标签: javascript reactjs react-hook-form


【解决方案1】:

假设目标是将所有选择保留在同一个 group 字段中,该字段必须是一个数组,按提供的顺序记录所选值,如果指定,自定义输入值作为最后一项,也许理想情况下会更容易在提交之前计算onSubmit中的值。

但是由于偏好不在onSubmit中添加逻辑,也许替代选项可以托管本地状态,在它更改时运行所需的计算,并手动调用setValue以将计算值同步到group字段.

修改后的分叉演示:codesandbox

import "./styles.css";
import { Controller, useFormContext } from "react-hook-form";
import React, { useState, useEffect } from "react";

export const Component = ({ name, ITEMS }) => {
  const { control, setValue } = useFormContext();
  const [state, setState] = useState({});

  useEffect(() => {
    const { custom, ...items } = state;
    const newItems = Object.entries(items).filter((item) => !!item[1]);
    newItems.sort((a, b) => a[0] - b[0]);
    const newValues = newItems.map((item) => item[1]);
    if (custom) {
      setValue(name, [...newValues, custom]);
      return;
    }
    setValue(name, [...newValues]);
  }, [name, state, setValue]);

  const handleCheck = (val, idx) => {
    setState((prev) =>
      prev[idx] ? { ...prev, [idx]: null } : { ...prev, [idx]: val }
    );
  };

  const handleCheckCustom = (checked) =>
    setState((prev) =>
      checked ? { ...prev, custom: "" } : { ...prev, custom: null }
    );

  const handleInputChange = (e) => {
    setState((prev) => ({ ...prev, custom: e.target.value }));
  };

  return (
    <Controller
      name={name}
      control={control}
      render={({ field, formState }) => {
        return (
          <>
            {ITEMS.map((item, index) => {
              const isCustomField = index === ITEMS.length - 1;
              return (
                <React.Fragment key={index}>
                  <label>
                    {item.id}
                    <input
                      type="checkbox"
                      name={name}
                      onChange={(e) =>
                        isCustomField
                          ? handleCheckCustom(e.target.checked)
                          : handleCheck(e.target.value, index)
                      }
                      value={item.value}
                    />
                  </label>
                  {typeof state["custom"] === "string" && isCustomField && (
                    <input onChange={handleInputChange} type="text" />
                  )}
                </React.Fragment>
              );
            })}
          </>
        );
      }}
    />
  );
};

【讨论】:

    【解决方案2】:

    好的,过了一会儿我得到了解决方案。我分叉了你的沙箱并做了一些小改动,在这里查看:Save Form values in ReactJS using checkboxes

    基本上,您应该有一个内部复选框状态并且也不要在表单中注册输入,因为这会将输入值添加到数组的末尾,无论该值是否为“”。

    这是代码:

    import "./styles.css";
    import { Controller, useFormContext } from "react-hook-form";
    import { useEffect, useState } from "react";
    
    export const Component = ({ name, ITEMS }) => {
      const { control, setValue } = useFormContext();
      const [state, setState] = useState(false);
      const [checkboxes, setCheckboxes] = useState(
        ITEMS.filter(
          (item, index) => index !== ITEMS.length - 1
        ).map(({ value }, index) => ({ value, checked: false }))
      );
      useEffect(() => {
        setValue(name, []); //To initialize the array as empty
      }, []);
    
      const [inputValue, setInputValue] = useState("");
    
      const handleChangeField = (val) => {
        const newCheckboxes = checkboxes.map(({ value, checked }) =>
          value == val ? { value, checked: !checked } : { value, checked }
        );
        setCheckboxes(newCheckboxes);
    
        const response = newCheckboxes
          .filter(({ checked }) => checked)
          .map(({ value }) => value);
        return state && !!inputValue ? [...response, inputValue] : response;
      };
    
      const handleChangeInput = (newInputValue) => {
        const response = checkboxes
          .filter(({ checked }) => checked)
          .map(({ value }) => value);
        if (state) if (!!newInputValue) return [...response, newInputValue];
        return response;
      };
    
      return (
        <Controller
          name={name}
          control={control}
          render={({ field, formState }) => {
            return (
              <>
                {ITEMS.map((item, index) => {
                  return (
                    <>
                      <label>
                        {item.id}
                        <input
                          type="checkbox"
                          name={`${name}[${index}]`}
                          onChange={(e) => {
                            if (index === ITEMS.length - 1) {
                              setState(e.target.checked);
                              return;
                            }
                            field.onChange(handleChangeField(e.target.value));
                          }}
                          value={item.value}
                        />
                      </label>
                      {state && index === ITEMS.length - 1 && (
                        <input
                          value={inputValue}
                          onChange={(e) => {
                            setInputValue(e.target.value);
                            field.onChange(handleChangeInput(e.target.value));
                          }}
                          type="text"
                        />
                      )}
                    </>
                  );
                })}
              </>
            );
          }}
        />
      );
    };
    

    【讨论】:

      【解决方案3】:

      要解决此问题,您可以更改 handleCheck 函数中的逻辑,以便仅在未选中最后一个复选框时才返回自定义值。这是 handleCheck 函数的更新版本:

      const handleCheck = (val) => {
        const { [name]: ids } = getValues();
        if (val !== "custom") {
          const response = ids?.includes(val)
            ? ids?.filter((id) => id !== val)
            : [...(ids ?? []), val];
          return response;
        }
      };
      

      此外,您需要更改文本输入的 onChange 处理程序以仅在复选框被选中时更新状态,以便自定义值不包含在数据中。您可以通过在文本输入的 onChange 处理程序中添加一个 if 语句来做到这一点:

      {
        state && index === ITEMS.length - 1 && (
          <input
            {...control.register(`${name}[${index}]`)}
            type="text"
            onChange={(e) => {
              if (state) {
                setState(e.target.value);
              }
            }}
          />
        );
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-04-17
        • 2020-05-06
        • 2011-01-10
        • 1970-01-01
        • 2013-09-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多