簡體   English   中英

如何將自定義下拉組件的值傳遞給 react-hook-form?

[英]How to pass a value from a custom dropdown component to react-hook-form?

我在這個 Gatsby 項目的 forms 中使用react-hook-form進行驗證,但我的下拉組件不是<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>
  );
}

稍微解釋一下,道具是從渲染組件的react-hook-form中的<Controller>傳遞的。 該組件本身有一個 state 用於處理用戶單擊 header 時將打開的列表。非常簡單: const toggling = () => setIsOpen(;isOpen); 可以。 選項的值是從傳遞給父元素的 const 映射的,然后一旦選擇了某些東西,它就會像<select>一樣在 header 中發生。

顯示下拉菜單行為的 Gif

這是表單的代碼(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) 即使切換適用於單擊 header 或從列表中選擇一個選項,單擊外部也不會關閉它,如下所示。

顯示問題 #2 的 Gif

我不確定如何解決這些問題。 我相信問題 #1 需要一個鈎子來獲取子組件的選定值,但不知道這個鈎子是什么,也不知道如何將它與 react-hook-form 一起使用。 對於問題 #2,可能是一個 function 捕獲外部點擊,這將切換回打開的列表。 事情是我已經設法將 header 作為目標,但不是它的外部,或者“非標頭”部分,如果那是一件事的話。

編輯:設法使用此庫檢測外部點擊: react-outside-click-handler 一個簡單問題的絕佳解決方案。

編輯 2 :閱讀來自 @dshung1997 的評論,我意識到我沒有在此處粘貼父組件的代碼。 只是糾正這個。 謝謝!

如果您需要將 selectedOption 獲取到父項,首先您假設父項是真實來源,這就是為什么您必須為下拉列表提供 function。

假設我們使用 select 標簽,它會像這樣

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)和一個 function 來更新那個值(setValue)。

“目標是在父組件上獲取此 selectedOption 值,以便表單在提交時發布它?”。 只有父母知道表格何時提交。 下拉菜單不應該也不需要確定。 目標是告訴父級在值更改后立即更新值,而不是在提交時將值提供給父級。

因此,您需要向 Dropdown 提供一個 function 來更新該值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM