【问题标题】:How to pass a value from a custom dropdown component to react-hook-form?如何将自定义下拉组件中的值传递给 react-hook-form?
【发布时间】:2020-11-16 22:59:37
【问题描述】:

我使用react-hook-form 在这个 Gatsby 项目的表单中进行验证,但我的下拉组件不是<select> 标记,而是使用 div 和无序列表制作的自定义组件。这完全是一个设计选择,因为我们需要它非常定制。这里是组件

// Dropdown.js
import React, { useState } from "react";
import "../../sass/components/forms/dropdown.sass"


export function Dropdown({ preambulo, name, options, placeholder, value}) {
  const [isOpen, setIsOpen] = useState(false);
  const [selectedOption, setSelectedOption] = useState(null);
    
  const toggling = () => setIsOpen(!isOpen);

  const onOptionClicked = value => () => {
    setSelectedOption(value);
    setIsOpen(false);
  };

  return (
    <React.Fragment>
      <span>{preambulo}</span>
      <div 
        className={`
          dropdownHeader 
          ${isOpen === true ? 'open' : 'closed'} 
          `} 
          onClick={toggling}
          >
        {selectedOption || placeholder}
      </div>
      {isOpen && (
        <div 
          className="dropdownListContainer">
            <ul 
              className="dropdownList"
              name={name}
              id={name}
              value={value}
              >
              {options.map((option, i) => (
                <li 
                  className={`dropdownListItem item-${i}`} 
                  key={i} 
                  onClick={onOptionClicked(option)}>
                  {option}
                </li>
              ))}
              <hr />
            </ul>
        </div>
        )}
    </React.Fragment>
  );
}

稍微解释一下,props 是从 &lt;Controller&gt; 中的 react-hook-form 传递的,组件被渲染的地方。组件本身具有处理列表的状态,当用户单击标题时将打开该列表。很简单:const toggling = () =&gt; setIsOpen(!isOpen); 做到了。并且选项的值是从传递给父元素的 const 映射的,然后一旦选择了某些内容,它就会像 &lt;select&gt; 一样发生在标题中。

这是表单的代码(Edit2):

// contato.js
import React from "react";
import { useForm, Controller } from "react-hook-form";

//Form Components
import { Dropdown } from "../../components/forms/Dropdown"
import { Input, TextArea } from "../../components/forms/Input"

const ContatoFull = ({ className }) => {
 
  const cargos = [
    {//** values **/}
  ]

  const estados = [
    "Acre",
    "Alagoas",
    "Amazonas",
    "Amapá",
    "Bahia",
    "Ceará",
    "Distrito Federal",
    "Espírito Santo",
    "Goiás",
    "Maranhão",
    "Minas Gerais",
    "Mato Grosso do Sul",
    "Mato Grosso",
    "Pará",
    "Paraíba",
    "Pernambuco",
    "Piauí",
    "Paraná",
    "Rio de Janeiro",
    "Rio Grande do Norte",
    "Rondônia",
    "Roraima",
    "Rio Grande do Sul",
    "Santa Catarina",
    "Sergipe",
    "São Paulo",
    "Tocantins"
  ]
  const methods = useForm();
  const { handleSubmit, control, formState, errors } = methods;
  
  const onSubmit = data => {
    alert(JSON.stringify(data));
  };
  
  console.log(formState);

  return (
    <form 
      method="post"
      className={`${className !== 0 ? className : ''}`} 
      onSubmit={handleSubmit(onSubmit)}>
      
      <div
        className={`dropdownContainer cargo`}>
        <Controller
          name="cargo"
          control={control}
          render={({ onClick, name, value }) =>
          <Dropdown 
            onClick={e => onClick(e.target.value)}
            value={value}
            preambulo="Eu sou" 
            placeholder="Placeholder" 
            name={name}
            options={cargos}
            />
          }
        />
      </div>

      <div
        className={`dropdownContainer estado`}>
        <Controller
          name="estado"
          control={control}
          render={({ onClick, name, value }) =>
          <Dropdown 
            onClick={e => onClick(e.target.value)}
            value={value}
            name={name}
            preambulo="Estou em/no" 
            placeholder="acre" 
            options={estados}
              />
          }
        />
      </div>

      <div 
        className={`inputContainer empresa`}>
        <Controller 
          name="empresa" 
          type="text "
          control={control}
          defaultValue=""
          rules={{ required: true }}
          render={({ onChange, value, name, type, label }) =>
          <Input 
            onChange={e => onChange(e.target.value)}
            value={value}
            label="Nome da minha empresa" 
            type="text" />
          }
        />
        {errors.empresa && <span className="erro requerido">Campo obrigatório</span>}
        
        
      </div>

      {//** more input components //*^}

      <button 
        type="submit" 
        className="simpleButton primary submit button">Enviar</button>
    </form>
  )
}

export default ContatoFull

现在,我们要处理两个问题:1)如何将值传递给表单,因为我们没有使用标准标签进行选择; 和 2) 即使切换适用于单击标题或从列表中选择一个选项,单击外部也不会关闭它,如下所示。

我不确定如何解决这些问题。我相信问题 #1 需要一个钩子来获取子组件选择的值,但不知道这个钩子是什么,也不知道如何将它与 react-hook-form 一起使用。 对于问题 #2,也许是一个捕获外部点击的函数,它会切换回打开的列表。问题是我已经设法针对标题而不是它的外部,或者“非标题”部分,如果那是一件事的话。

编辑:设法使用此库检测到外部点击:react-outside-click-handler。一个简单问题的绝佳解决方案。

编辑 2:阅读@dshung1997 的评论我意识到我没有在此处粘贴父组件的代码。只是纠正这个。谢谢!

【问题讨论】:

  • 嗯。我认为将值传递给表单似乎很明显。如果没有,介意你多说几句吗?
  • 当然。在组件下拉菜单中,该值由所选选项设置,但我不能或无法在提交时将相同的值传递给控制台。我的意思是,表单没有从子组件中获得任何更改。这可能很明显,但我不明白。我不是一个编码员。 :(
  • 嗯。我认为您想将一个值传递给 Dropdown,当您选择一个项目时该值将被更新。正确的?如果是这样,您将需要两件事。值和更新它的函数。就像普通的
  • 如果我误解了这一点,我很抱歉,但不是反过来吗?子组件&lt;Dropdown&gt;[selectedOption, setSelectedOption] = useState(null) 中已经有这样的功能(或类似的东西)来设置“初始选项”,然后onOptionClick = value =&gt; () =&gt; {...} 更改 selectedOption 的状态,因此它会正确显示在标题上,你知道,模拟&lt;select&gt; 行为。我想知道这是否与您告诉我的相同,或者我是否错了。目标是在父组件上获取这个 selectedOption 值,以便表单在提交时发布它?
  • 在 Dropdown 中拥有本地状态主要取决于您的偏好。但是,是的,我明白你的意思。让我写一个答案

标签: javascript reactjs react-hook-form


【解决方案1】:

如果您需要将 selectedOption 获取到父级,首先您假设父级是事实的来源,这就是您必须为 Dropdown 提供函数的原因。

假设我们使用一个选择标签,它会是这样的

export const Parent = () => {
  const [value, setValue] = useState("1");
  return (
    <select value={value} onChange={(e) => setValue(e.target.value)}>
      <option value="1">1</option>
      <option value="2">2</option>
      <option value="3">3</option>
    </select>
  );
};

所以我们需要一个值的变量(value)和一个更新该值的函数(setValue)。

“目标是在父组件上获取这个 selectedOption 值,以便表单在提交时发布它?”。只有父母知道何时提交表单。下拉列表不应该也不需要确定。目标是告诉父级在更改值后立即更新值,而不是在提交时将值提供给父级。

因此,您需要为 Dropdown 提供一个更新值的函数。

【讨论】:

  • 好的!我相信我现在明白了。将在接下来的几个小时内对此进行处理,并将返回结论 - 或者没有。无论如何感谢您的回答!
  • 没问题。让我知道你的结果:)
  • 只是给你我的反馈:不能让它工作。使用一个库来呈现一个工作下拉列表。还是谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-11
  • 2020-10-25
  • 2021-04-29
  • 1970-01-01
  • 2022-10-16
  • 1970-01-01
相关资源
最近更新 更多