React hook form setValue returns undefined with multi select (react-select)

React hook form setValue returns undefined with multi select (react-select)

我正在尝试使用 React 和来自 react-select 的 select 元素构建一个表单,并使用 react-hook-form 完成验证。

更改后值似乎成功更改,但是当我提交表单时 select 值未定义。

我有输入字段,它们工作正常,但对于我的 select 和多 select 元素,它失败了。

React 的新手很困惑,试图找到这个问题的答案但找不到。删除了一些代码以保留有趣的部分,希望我做对了。

import React, { useState } from "react";
import Collapsible from "../collapsible/Collapsible"
import Select, { components } from 'react-select';
import axios from "axios";
import { useInput } from "../../hooks"
import { useForm } from 'react-hook-form';
import { selectStyles } from "./styles"
import allBots  from "../../data/bots";
import platforms from "../../data/platforms";
import connectionTypes from "../../data/connection_types";
import generatePayload from "./generate_payload";

const Connect = () => {
  const { setValue, register, handleSubmit, errors } = useForm();

  const [values, setValues] = useState({
    selectedOption: []
  });
  
  const CustomOption = props => {
    return (
      <components.Option {...props}>
        <div style={{ display: "inline-block", "min-width": "40px" }}>{props.data.suffix}</div>
        <div style={{ display: "inline-block" }}>{props.data.value}</div>
      </components.Option>
    );
  };

  const handleBotChange = selectedBot => {
    setValue("bots", selectedBot);
    setValues(selectedBot);
  };

  const handleMultiChange = selectedOption => {
    console.log(selectedOption);
    setValue("platform", selectedOption);
    setValues(selectedOption);
  };

  const onSubmit = (data) => {
    console.log(data);
    // outputs { "bots": undefined, "platform": undefined, username: "theinput" }
  }
  return (
    <Collapsible className="collapsible" header="Connect">
      <div className="collapsibleContent">
        <form onSubmit={handleSubmit(onSubmit)}>
          <Select
            className="reactSelect"
            name="platform"
            placeholder="Platform"
            value={values.platform}
            onChange={handleMultiChange}
            options={platforms}
            ref={e => register({ name: "platform", required: true })}
          />
          <Select
            isMulti
            name="bots"
            options={bots}
            onChange={handleBotChange}
            closeMenuOnSelect={false}
            blurInputOnSelect={false}
            className="basic-multi-select formItem"
            classNamePrefix="select" 
            components={{Option: CustomOption}}  
            styles={selectStyles}
            placeholder="Bots"
            ref={e => register({ name: "bots", required: true })}
          />
          <input
            name="username"
            ref={register({
              validate: value => value !== "illegal_value" || "Illegal value"
            })}
          />
          {errors.username && errors.username.message}

          <button type="submit">Submit</button>
        </form>
      </div>
    </Collapsible>
  );
}

export default Connect; 

更新: 我决定使用 react-hook-form 的“控制器”功能将其与 react-select 集成。这就是我的代码部分的结束方式。我还使用 SocketIO 事件来动态填充下拉列表中的选项。这个解决方案并不完美,但它确实不错。

import React, { useState, useEffect } from "react";
import Collapsible from "../collapsible/Collapsible"
import Select, { components } from 'react-select';
import { useForm, Controller } from 'react-hook-form';
import { selectStyles } from "./styles"
import connectionTypes from "../../data/connection_types";
import { OptionWithSuffix } from "../form_components/components";

import socket from "../socket";

const Connect = () => {
  const { errors, control, watch, getValues, handleSubmit, register, setValue, reset } = useForm();
  const watchFields = watch(["connectionType"]);
  const [bots, setBots] = useState([]);
  const connectionType = getValues("connectionType") || {};
  
  useEffect(() => {
    socket.on("bots", data => {
      setBots(data.map(obj => ({...obj, suffix: obj.count, value: obj.name, label: obj.name})));
    });
    return () => {
      socket.off("bots");
    };
  }, []);

  const onSubmit = (data) => {
    console.log(data);
  }
  
  return(  
    <Collapsible className="collapsible" header="Connect">
      <div>
        <form onSubmit={handleSubmit(data => onSubmit(data))}>    
        <Controller
          control={control}
          as={<Select className="formItem" options={platforms} />}
          rules={{ required: true }}
          placeholder="Select platform"
          name="platform"
          styles={selectStyles}
          register={register}
          setValue={setValue}
        />
        <Controller
          control={control}
          as={<Select className="formItem" options={connectionTypes}/>}
          rules={{ required: true}}
          placeholder="Select connection type"
          name="connectionType"
          styles={selectStyles}
          register={register}
          setValue={setValue}
        />
        {errors.connectionType && "Connection type is required"}
        {connectionType.value === "specific" &&
          <Controller
          control={control}
          as={
            <Select 
              isMulti 
              className="formItem" 
              options={bots}
              closeMenuOnBlur={false}
              closeMenuOnSelect={false}
              components={{Option: OptionWithSuffix}}  
              styles={selectStyles}
            />
          }
          rules={{ required: true }}
          placeholder="Select bots"
          name="bots"
          rules={{ required: true }}
          register={register}
          setValue={setValue}
        />
        }
        {errors.platform && connectionType.value === "specific" && "Bots are required"}    
        <input className="formItem" type="submit" placeholder="Connect"/>
      </form>
    </div>
  </Collapsible>
  );
}
export default Connect;  

我不能完全测试你的代码,因为它有一些导入。

据我所知,您需要在更改事件中获取 selected 选项值。

  const handleBotChange = selectedBot => {
    setValue("bots", selectedBot.value);
  };

  const handleMultiChange = selectedOption => {
    setValue("platform", selectedOption.value);
  };

另外,我不太了解react-select控件,但它可能是内部控制的,所以你不需要values state hook

See here

看看 react-hook-form 的文档,它们展示了如何注册第三方 ui 组件的正确方法,甚至有一个使用 react-select [=12= 的示例]

下面是我如何使用 v6.14.1.

中的 react-hook-forms 让我的 MUI-Select 获取多个值
<Controller
    name="categories"
    type="select"
    control={control}
    defaultValue={categoryOptions[0]}
    render={({ onChange, onBlur, value }) => (
        <RHFSelect
             onChange={onChange}
             onBlur={onBlur}
             value={[...value]}  // this is what you need to do
             multiple={true}
             options={categoryOptions}
        />
    )}
/> 

RHFSelect 在我的代码中是可重复使用的 Select。要select只有一个值,你可能会喜欢这样-

value={value}

对于v7.9.0,可以这样做

<Controller
   name="colors"
   type="select"
   control={control}
   defaultValue={[]}
   render={({ field }) => (
      <Select
         {...field}
         multiple
      >
      /* Select ops */
      </Select>
   )}
/>