简体   繁体   English

反应钩子形式 setValue 返回未定义的多 select (react-select)

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

I'm trying to build a form using React with select elements from react-select and the validation done using react-hook-form.我正在尝试使用 React 和来自 react-select 的 select 元素构建一个表单,并使用 react-hook-form 完成验证。

Upon change the value seems to successfully change, but when I submit the form the select value is undefined.更改后,该值似乎已成功更改,但是当我提交表单时,select 值未定义。

I have input fields and those work properly, but for my select and multi-select elements it fails.我有输入字段并且它们工作正常,但是对于我的 select 和多选元素它失败了。

New to React so very confused, tried to find an answer to this but not able to. React 新手非常困惑,试图找到答案但无法找到。 Removed some of the code to keep the interesting parts, hopefully I got it right.删除了一些代码以保留有趣的部分,希望我做对了。

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; 

Update: I decided to use the "Controller" functionality of react-hook-form to integrate it with react-select.更新:我决定使用 react-hook-form 的“控制器”功能将其与 react-select 集成。 This is how part my code ended up.这就是我的代码部分的结束方式。 I also use SocketIO events in order to dynamically populate the options in the dropdown list.我还使用 SocketIO 事件来动态填充下拉列表中的选项。 This solution isn't perfect but it's something.这个解决方案并不完美,但它是一些东西。

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;  

I cant quite test your code, since it has a few imports.我无法完全测试您的代码,因为它有一些导入。

From what I can see, you will need to get the selected option value on the change event.据我所知,您需要在更改事件中获取选定的选项值。

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

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

Also, I do not really know the react-select control, but it might be controlled internally, so you do not need the values state hook另外,我真的不知道 react-select 控件,但它可能是内部控制的,所以你不需要值 state 钩子

See here 看这里

take a look at the documentation of react-hook-form , they show the proper way on how to register third party ui components, even has a example using react-select Integrating with UI libraries看看react-hook-form的文档,它们展示了如何注册第三方 ui 组件的正确方法,甚至还有一个使用react-select 与 UI 库集成的示例

Here's how I made my MUI-Select take multiple values using react-hook-forms in v6.14.1 .以下是我如何使用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 is a reusable Select in my code. RHFSelect在我的代码中是一个可重复使用的Select To select only a single value, you may do to like this -对 select 只有一个值,你可以这样做 -

value={value}

For v7.9.0 , it can be done like this对于v7.9.0 ,可以这样做

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM