如何使用 Material-UI Select 和 React Hook Form

Yun*_*gHK 14 ref reactjs material-ui react-hook-form

我已经使用 MUI 和 React Hook Form 在 React 中构建了一个表单。我正在尝试创建一个用作Select Input的自定义TextField元素。我希望它是一个带有Ref道具的不受控制的组件。我试图按照 MUI 和 React Hook Form 文档的建议传递 inputRef 道具,但没有成功。

            <TextField
              id="id"
              name="name"
              select
              native="true"
              className={classes.textField}
              label="label"
              margin="normal"
              variant="outlined"
              inputRef={register({ required: "Choose one option" })}
              error={!!errors.name}
            >
              <MenuItem value="">Choose one option</MenuItem>
              <MenuItem value="3">03</MenuItem>
              <MenuItem value="6">06</MenuItem>
              <MenuItem value="9">09</MenuItem>
              <MenuItem value="12">12</MenuItem>
              <MenuItem value="16">16</MenuItem>
              <MenuItem value="18">18</MenuItem>
            </TextField>
Run Code Online (Sandbox Code Playgroud)

我发现的一件事是,如果我将本机selectref 一起使用,它就可以正常工作。
此外,我尝试将inputRef道具更改为SelectProps道具,但它也不起作用。

先感谢您。

Nea*_*arl 26

RHF v7 更新

Select下面是RHF 形式的Material UI 的最小代码示例:

const { formState, getValues, watch, register, handleSubmit } = useForm();
const { errors } = formState;
Run Code Online (Sandbox Code Playgroud)
<TextField
  select
  fullWidth
  label="Select"
  defaultValue=''
  inputProps={register('currency', {
    required: 'Please enter currency',
  })}
  error={errors.currency}
  helperText={errors.currency?.message}
>
  {currencies.map((option) => (
    <MenuItem key={option.value} value={option.value}>
      {option.label}
    </MenuItem>
  ))}
</TextField>
Run Code Online (Sandbox Code Playgroud)

Codesandbox 演示


abu*_*ick 23

使用带有反应钩子形式的 Material-ui 中的 Select 组件需要您使用控制器实现自定义逻辑https://react-hook-form.com/api#Controller

这是一个可重用的组件,有望简化在您的应用程序中使用该 Select 组件的代码:

import FormControl from "@material-ui/core/FormControl";
import InputLabel from "@material-ui/core/InputLabel";
import Select from "@material-ui/core/Select";
import { Controller } from "react-hook-form";

const ReactHookFormSelect = ({
  name,
  label,
  control,
  defaultValue,
  children,
  ...props
}) => {
  const labelId = `${name}-label`;
  return (
    <FormControl {...props}>
      <InputLabel id={labelId}>{label}</InputLabel>
      <Controller
        as={
          <Select labelId={labelId} label={label}>
            {children}
          </Select>
        }
        name={name}
        control={control}
        defaultValue={defaultValue}
      />
    </FormControl>
  );
};
export default ReactHookFormSelect;
Run Code Online (Sandbox Code Playgroud)

您可以像这样在您的应用程序中使用它:

import FormControl from "@material-ui/core/FormControl";
import InputLabel from "@material-ui/core/InputLabel";
import Select from "@material-ui/core/Select";
import { Controller } from "react-hook-form";

const ReactHookFormSelect = ({
  name,
  label,
  control,
  defaultValue,
  children,
  ...props
}) => {
  const labelId = `${name}-label`;
  return (
    <FormControl {...props}>
      <InputLabel id={labelId}>{label}</InputLabel>
      <Controller
        as={
          <Select labelId={labelId} label={label}>
            {children}
          </Select>
        }
        name={name}
        control={control}
        defaultValue={defaultValue}
      />
    </FormControl>
  );
};
export default ReactHookFormSelect;
Run Code Online (Sandbox Code Playgroud)

这是您的 codeSandBox 使用此组件更新,用于信息表单中的选择:

https://codesandbox.io/s/unit-multi-step-form-kgic4?file=/src/Register/Information.jsx:4406-5238

  • 为了避免以下错误,您需要确保传递有效的defaultValue:`Material-UI:组件正在将Select的不受控制值状态更改为受控制。元素不应从不受控制切换到受控制(反之亦然)。在组件的生命周期内决定使用受控或非受控的 Select 元素。 (3认同)
  • 在这种情况下,我们没有 onChange ,因为输入是由 DOM 处理的,即不受控制的组件。 (2认同)

Kos*_*nos 10

接受的版本是正确的但已过时。

至少在我使用的版本中:"react-hook-form": "^7.30.0"您应该使用该render参数。

这是非常适合我的“更新”版本:

        <FormControl>
          <InputLabel id="level-label">Level</InputLabel>
          <Controller
            name="level"
            id="level"
            defaultValue={level}
            control={control}
            render={({ field }) => (
              <Select labelId="level-label" {...field}>
                <MenuItem value={0}>0</MenuItem>
                <MenuItem value={1}>1</MenuItem>
              </Select>
            )}
          />
          <FormHelperText error={true}>{errors.level?.message}</FormHelperText>
        </FormControl>

Run Code Online (Sandbox Code Playgroud)

这里重要的是将field属性向下传播到子元素(在我们的例子中为Select )

附言。我认为你不需要一个单独的组件,它非常简单。

[更新] 这是我的一个对话框的完整代码。应德山要求。

import {
  Box, Chip, FormControl, Input, Stack,
} from '@mui/material';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import debounce from '../@utils/debounce';
import useRawParams from '../@utils/useRawParams';
import { useBrandsSearchQuery } from '../data/products';
import { SearchRoute } from '../SBRoutes';
import LoadingDiv from './LoadingDiv';
import SBDialog from './SBDialog';
import { useSearchBarContext } from '../contexts/SearchBarContext';

const context = { suspense: false };
/**
 * Show the modal dialog with the list of brands, and search box for it
 * Eeach brand will be as a link, for the SEO purposes
 */
export default function AllBrandsDialog({ open, setOpen }) {
  const [t] = useTranslation();
  const [query, setQuery] = useState('');
  const [brands, setBrands] = useState([]);
  const params = useRawParams(true);
  const paramsBrands = params.brands?.split(',') || [];
  const { setFilterActive } = useSearchBarContext();

  const variables = useMemo(() => (query.length ? {
    filterText: query,
  } : null), [query]);

  const [{ data, fetching: loading }] = useBrandsSearchQuery({ variables, pause: Boolean(!variables), context });
  const debounceSetQuery = useCallback(debounce(200, (text) => {
    setQuery(text);
  }));

  useEffect(() => {
    if (!data || !open) return;
    setBrands(data.brands || []);
  }, [data, open]);

  return (
    <SBDialog open={open} setOpen={setOpen} title={t('Search and select a brand')}>
      <Stack direction="column" spacing={2}>
        <FormControl>
          <Input
            id="tagSearch"
            placeholder={t('Start typing to see the brands')}
            onChange={(e) => debounceSetQuery(e.target.value)}
            autoFocus={true}
          />
        </FormControl>
        <Box display="grid" width={220} height={300} overflow="auto" gap={1} position="relative">
          {brands?.map((brand) => (
            <Chip
              component={Link}
              key={brand.id}
              disabled={paramsBrands.indexOf(brand.url) > -1}
              to={SearchRoute.generatePath({
                ...params,
                brands: [...paramsBrands, brand.url],
                page: undefined,
              })}
              size="small"
              label={brand.nicename}
              variant="outlined"
              onClick={() => {
                setOpen(false);
                setFilterActive(false);
              }}
              clickable={true}
            />
          ))}
          {loading && <LoadingDiv modal={true} />}
        </Box>
      </Stack>
    </SBDialog>
  );
}

AllBrandsDialog.propTypes = {
  open: PropTypes.bool.isRequired,
  setOpen: PropTypes.func.isRequired,
};
Run Code Online (Sandbox Code Playgroud)


小智 5

这是我的工作代码,希望它可以帮助,需要使用 setValue

  <TextField
    fullWidth
    inputRef={register({
      name: 'name',
    })}
    select
    onChange={e => setValue('name', e.target.value, true)}
    label={label}
    defaultValue={defaultValue}
  >
    {options.map((option) => (
      <MenuItem key={option.label} value={option.value}>
        {option.label}
      </MenuItem>
    ))}
  </TextField>
Run Code Online (Sandbox Code Playgroud)

这里使用原生select,不需要setValue,但是value总是字符串

<TextField
    fullWidth
    select
    SelectProps={{
      native: true,
      inputProps: { ref: register, name: 'name' }
    }}
    label={label}
    defaultValue={defaultValue}
  >
    {options.map((option) => (
      <option key={option.label} value={option.value}>
        {option.label}
      </option>
    ))}
  </TextField>
Run Code Online (Sandbox Code Playgroud)