Material-ui: 使用 react-hook-form

创建于 2019-11-08  ·  40评论  ·  资料来源: mui-org/material-ui

我正在尝试用react-hook-formmaterial-ui做一个简单的表格。 我希望能够:

  1. 提交/验证表单
  2. 正确重置表格
  3. 正确加载数据

抱歉,错误的问题搜索,它看起来像是https://github.com/mui-org/material-ui/issues/17018的副本

  • [x] 该问题存在于最新版本中。
  • [x] 我已经搜索了这个存储库的问题,并相信这不是重复的。

目前的行为😯

第一步工作正常。
reset函数清空了值,但是material-ui TextField的状态好像一直停留在“填充”,样式和清空的数据不匹配。
具有初始值的 reset 函数正确设置了值,但行为与 reset 相同(好吧,在这种情况下相反),数据已加载但 TextField 的内部状态未“填充”。

预期行为🤔

当加载/清除数据时,material-ui 输入应刷新其内部状态

繁殖步骤🕹

你可以试试这个 CodePen 上的三个按钮:
https://codesandbox.io/s/material-demo-ywutu

  1. 提交:可以正确验证数据。
  2. 正确重置但 TextFields 的状态不为空。
  3. 正确加载数据但未填充 TextFields 的状态。

我在 react-hook-form 项目中提交了一个问题,但所有者告诉我也在这里打开一个,所以我们在这里:)

enhancement external dependency

最有用的评论

我添加了waiting for users upvotes标签。 我正在结束这个问题,因为我们不确定人们是否正在寻找这种支持。 所以如果你是,请upvote这个问题。 我们将根据赞成票的数量优先考虑我们的努力。

https://npm-stat.com/charts.html?package=formik&package=react-hook-form&package=react-final-form

所有40条评论

我添加了waiting for users upvotes标签。 我正在结束这个问题,因为我们不确定人们是否正在寻找这种支持。 所以如果你是,请upvote这个问题。 我们将根据赞成票的数量优先考虑我们的努力。

https://npm-stat.com/charts.html?package=formik&package=react-hook-form&package=react-final-form

非常需要 :pray::pray::pray: 任何解决方法?

非常需要!

绝对需要。

知道解决方案是什么吗?

我不确定具体的东西,但我知道我开始使用这两个库,我真的很想看到两者之间更紧密的集成。 React Hook Form 和 Material UI 结合得很好。 他们对 TypeScript 的支持也很好。

嘿, @oliviertassinari感谢您查看它。 看起来 MUI TextField现在与原生输入的 API 一起工作得更好! 这是非常好的。 当您调用e.target.value = 'xxx'时,我们曾经将占位符文本覆盖在文本顶部。 👍

Screen Shot 2019-11-20 at 10 06 26 am

https://codesandbox.io/s/react-hook-form-conditional-fields-delete-1frsm
在上面的示例中, <Switch />在调用reset API 后没有重置。

如果我们可以让所有与表单相关的组件都支持原生表单输入 API(通过输入的引用设置和重置值),那将会非常好需要考虑的事情(也许直到 lib 变得更受欢迎❤️)

再次感谢您花费时间和精力来研究这个问题(维护像 MUI 这样的巨大开源项目对我来说很疯狂,而您对 React 社区所做的工作非常出色)。 最重要的是,让我知道我是否也可以使用 MUI 进行任何工作。

干杯
账单

@bluebill1049感谢您查看。 图书馆似乎获得了很好的吸引力,但它仍然远远落后于 formik。 开关复位是我们唯一的问题吗?

谢谢, @oliviertassinari重新打开并调查这个问题。 🙏 是的,你说得对react-hook-form仍然是镇上的一个新男孩,如果你想再等一段时间看看是否有更多的用户采用react-hook-form ,你就没有行动我认为这是完全合理的(即使在我内心深处,我也非常希望它与 MUI 一起工作,哈哈哈太自私了😼)。 与此同时,我将创建一个与react-hook-form兼容的输入表(甚至可能还有一个代码和框)我也会在这里发布它。

如果在 react-hook-form 和使用量越来越大的情况下工作良好,也许我们可以考虑使用原生输入 API(这是 React hook 表单在幕后使用的),获得input会很棒, selectradiocheckbox是表单的主要用法。

仅供参考@oliviertassinari我正在努力缩小 MUI 和 react-hook-form 之间的差距。 https://github.com/react-hook-form/react-hook-form-input

还面临重置和加载默认数据的问题。 请研究一下这个问题。 或者至少现在提供一个解决方法。

@raikusy请看看我上面发布的链接,我想它会帮助你。 从长远来看,MUI 组件支持原生表单 api 'reset' 和通过组件 'ref' 更新值会很棒,但这是一个很大的工作成本,我相信 MUI 有很多功能和想法排队哪个更重要。 正如我们上面讨论的,直到 react hook 形式成为主流 lib 或变得流行,然后 MUI 将开始研究解决方案。 希望这些是有道理的 :) 感谢您也使用 react hook 形式❤️🤩🤟🏻

仅供参考,包装器组件仍将使您的表单能够重新渲染 0 次。 因为输入状态更新是在包装组件中隔离的。🤩🤟🏻💃

谢谢@bluebill1049这看起来很棒。 我有一个条件,当数据从 props 传递时,表单字段将具有默认值。 那么 TextField 组件是否也适用于上述解决方案? 我也在寻找任何更好的例子来将带有道具的默认值传递给表单。 道具有时可能来自已安装表单后的 api 调用。

@raikusy您应该能够将 reset/setValue API 与包装器组件一起使用。
重置:对于整个表单 -> https://react-hook-form.com/api#setValue
setValue:对于个人 - > https://react-hook-form.com/api#reset

我无法让<Slider />与 RHF 一起工作。

像这样使用它:

      <FormControl fullWidth component="fieldset" margin="normal">
        <FormLabel component="legend">Palavras</FormLabel>
        <RHFInput
          as={<Slider min={100} max={1200} step={100} valueLabelDisplay="auto" marks={marks} />}
          type="input"
          name="words"
          register={register}
          setValue={setValue}
        />
      </FormControl>

我收到以下错误:

Warning: Failed prop type: Invalid prop `value` supplied to `ForwardRef(Slider)`.
    in ForwardRef(Slider) (created by WithStyles(ForwardRef(Slider)))
    in WithStyles(ForwardRef(Slider)) (created by SetupAccountForm)
    in Unknown (created by SetupAccountForm)
    in fieldset (created by ForwardRef(FormControl))
    in ForwardRef(FormControl) (created by WithStyles(ForwardRef(FormControl)))
    in WithStyles(ForwardRef(FormControl)) (created by SetupAccountForm)
    in form (created by SetupAccountForm)
    in SetupAccountForm (created by SetupAccountPage)
    in div (created by ForwardRef(CardContent))
    in ForwardRef(CardContent) (created by WithStyles(ForwardRef(CardContent)))
    in WithStyles(ForwardRef(CardContent)) (created by SetupAccountPage)
    in div (created by ForwardRef(Paper))
    in ForwardRef(Paper) (created by WithStyles(ForwardRef(Paper)))
    in WithStyles(ForwardRef(Paper)) (created by ForwardRef(Card))
    in ForwardRef(Card) (created by WithStyles(ForwardRef(Card)))
    in WithStyles(ForwardRef(Card)) (created by SetupAccountPage)
    in div (created by ForwardRef(Grid))
    in ForwardRef(Grid) (created by WithStyles(ForwardRef(Grid)))
    in WithStyles(ForwardRef(Grid)) (created by Layout)
    in Layout (created by SetupAccountPage)
    in SetupAccountPage (created by App)
    in Route (created by App)
    in Switch (created by App)
    in Router (created by HashRouter)
    in HashRouter (created by App)
    in App (created by Root)
    in div (created by ForwardRef(Container))
    in ForwardRef(Container) (created by WithStyles(ForwardRef(Container)))
    in WithStyles(ForwardRef(Container)) (created by Root)
    in div (created by Styled(MuiBox))
    in Styled(MuiBox) (created by Root)
    in Provider (created by Root)
    in Root Button.js:233:15

@hbarcelos看起来滑块不支持 value props :( 我会对此进行一些调查。同时,您可能必须使用自定义寄存器,该寄存器位于useEffect

非常感谢@bluebill1049

我最终做了这样的事情:

import React, { useEffect, useCallback, useState, useMemo } from 'react';
import t from 'prop-types';
import clsx from 'clsx';
import { makeStyles } from '@material-ui/core/styles';
import Box from '@material-ui/core/Box';
import Slider from '@material-ui/core/Slider';

const useStyles = makeStyles(theme => ({
  sliderWrapper: {},
  sliderLabel: {
    marginTop: theme.spacing(1),
    marginBottom: theme.spacing(0.5),
    color: theme.palette.primary.main,
  },
}));

function CustomSlider({
  register,
  unregister,
  setValue,
  name,
  rules,
  defaultValue,
  valueLabelAs,
  formatLabel,
  ...rest
}) {
  const cl = useStyles();

  const [currentValue, setCurrentValue] = useState(defaultValue);

  useEffect(() => {
    register({ name });
    return () => unregister(name);
  }, [defaultValue, name, register, setValue, unregister]);

  const valueLabel = useMemo(() => {
    if (!valueLabelAs) {
      return null;
    }

    return React.cloneElement(
      valueLabelAs,
      { className: clsx(valueLabelAs.props.className, cl.sliderLabel) },
      formatLabel(currentValue)
    );
  }, [cl.sliderLabel, currentValue, formatLabel, valueLabelAs]);

  const handleChange = useCallback(
    (_, value) => {
      setValue(name, value);
      setCurrentValue(value);
    },
    [name, setValue]
  );

  return (
    <React.Fragment>
      {valueLabel}
      <Box className={cl.sliderWrapper}>
        <Slider {...rest} onChange={handleChange} defaultValue={defaultValue} />
      </Box>
    </React.Fragment>
  );
}

CustomSlider.defaultProps = {
  rules: {},
  defaultValue: '',
  valueLabelAs: null,
  formatLabel: v => v,
};

CustomSlider.propTypes = {
  register: t.func.isRequired,
  unregister: t.func.isRequired,
  setValue: t.func.isRequired,
  name: t.string.isRequired,
  rules: t.object,
  defaultValue: t.oneOfType([t.number, t.string]),
  valueLabelAs: t.node,
  formatLabel: t.func,
};

export default CustomSlider;

它按预期工作。

也很需要这个

从长远来看,MUI 组件支持原生表单 api 'reset' 和通过组件 'ref' 更新值将是很好的。

@bluebill1049这不是 React 或 react-hook-form 本身的问题吗? 开发人员应该如何使用 react-hook-form 对输入状态的变化做出“反应”?
假设我是一个构建自定义输入的 React 开发人员,我想在元素被填充/为空时以不同的方式设置元素的样式,我应该如何实现? 似乎从未触发过更改事件。

当你的库在 React 的声明性模型“外部”更改输入值时,你是否考虑过触发更改事件? 就像在@test-library/react 中完成的一样?

这将支持依赖本机输入元素的 Material-UI 组件,对于其他组件,如 Slider,仍然需要自定义包装器。 滑块有一个 value 属性,但 onChange 的签名与本机输入不同。 我认为看到一个集成库会很棒,因为我们有其他表单库:#15585。


我想知道为什么这个问题得到了这么多的赞成,我想我已经发现了🙃:

Capture d’écran 2019-12-07 à 20 41 31
https://react-hook-form.com/advanced-usage#ControlledmixedwithUncontrolledComponents

@oliviertassinari ,感谢您再次关注这个问题👍

假设我是一个构建自定义输入的 React 开发人员,我想在元素被填充/为空时以不同的方式设置元素的样式,我应该如何实现? 似乎从未触发过更改事件。

我会在我自己的使用本机输入的项目中寻求 CSS 选择器的解决方案,例如:选择具有空值的元素并显示样式。 另一种方法是使用 react-hook-form watch API 来检测空值并作为prop向下传递(使用我包装的输入组件)。

当你的库在 React 的声明性模型“外部”更改输入值时,你是否考虑过触发更改事件? 就像在@test-library/react 中完成的一样?

是的,我们几乎是在react-hook-form-input下做的。 😄

经过多年的构建表单控制后,我相信用于构建表单的不受控制的组件,它确实让事情变得更容易。 它可能无法解决所有极端情况(如果您在 Facebook LOL 工作),但在构建表单时,它确实使我的工作生活和我周围的其他人更轻松。 HTML 输入本身是有状态的,我很乐意在这个库中包含它们(不是说它是对还是错,而是一种替代解决方案)。

我非常喜欢构建 React 应用程序,这就是为什么围绕它构建大量包的原因。 我理解 React 拥抱受控组件。 然而, react-hook-form 并没有阻止开发人员构建表单控制,因为您仍然可以在useEffect处自定义register useEffect

结论:

我想我们可以关闭这个问题,我会删除我网站上与这个问题相关的链接。 最重要的是,我可以更新此页面以包含react-hook-form-input吗?

https://material-ui.com/components/text-fields/#complementary -projects

是的,我们几乎是在 react-hook-form-input 下做的。

@bluebill1049这听起来与我的建议相反。 我明白以下几点:

  • 反应钩形式:

    1. 它跟踪输入的 DOM 节点。 当它需要更改值时,它会执行input.value = 'x' 。 这对 React 来说是有问题的,因为它无法知道输入值已更改。 例如,不会触发任何更改事件

    2. 因为 react-hook-form 需要监听 input 值的变化,所以它在 input 上设置了一个“input”事件监听值从受控输入更改时,React 的更改不会触发任何事件。

  • react-hook-form-input:库控制输入以解决 react-hook-form 的两个先前限制(i 和 ii)。

鉴于 1. React 不太可能很好地支持 react-hook-form 方法和 2. react-hook-form 不能在带有受控伪非受控输入(改变默认值和只听 onChange 的值),我建议:

  1. react-hook-form 在内部修补了受控和伪不受控制的问题。 基本上,您需要解决i.ii. (请参阅链接代码和框中的建议解决方案:dispatch +defineProperty)。
  2. Material-UI 寻求社区的帮助,在需要时为 react-hook-form 提供适配器(与 #15585 相关)。
  3. 我们关闭这个 issue 👌

谢谢@oliviertassinari的详细回复。 我将对您提出的解决方案进行进一步调查:)

我相信你上面提到的 React 的大部分问题是当你切换受控组件时,这是我想要改进的部分。

只是要清楚:

  • 要重现更改问题,请转到 https://codesandbox.io/s/heuristic-galileo-1wf8c 并单击按钮 => 丢失日志。 现在,前往https://codesandbox.io/s/silly-allen-72zz7看看调度方法如何解决问题。
  • 要重现更改问题,请转到 https://codesandbox.io/s/elastic-agnesi-osuuy 并单击按钮 => 丢失日志。 现在,前往https://codesandbox.io/s/sparkling-rain-3rebh ,看看 Object.defiendProperty 方法如何解决这个问题。

所以我很确定你可以用 react-hook-form 解决这个问题。 如果您可以尝试/部署这些修复程序并从您的文档中消除对我们的指责,我们将不胜感激😛。

这些更改应该真的有助于吸引您的表单库,我希望您会喜欢它:D。

我相信你上面提到的 React 的大部分问题是当你从不受控制的组件切换到受控或受控组件时,这是我想要改进的部分。

当用户在不受控制和受控制之间切换时,React 会发出警告。 我不明白你的意思。

所以我很确定你可以用 react-hook-form 解决这个问题。 如果您可以尝试/部署这些修复程序并从您的文档中消除对我们的指责,我们将不胜感激😛。

我绝对不会责怪 MUI 哈哈,我爱 MUI(和我的小星星 ⭐️)。 如您所知,react-hook-form 是镇上的新成员,我试图在特定问题上获得一些关注或动力。 如果你发现那样(责备),我道歉。 再次非常感谢您的帮助和调查。

image
(不是责备)

我相信你上面提到的 React 的大部分问题是当你从不受控制的组件切换到受控或受控组件时,这是我想要改进的部分。

当用户在不受控制和受控制之间切换时,React 会发出警告。 我不明白你的意思。

抱歉,我的意思是受控组件 :) 修复了我的评论

@bluebill1049 太棒了:)

非常感谢, @oliviertassinari (你很友善)

请注意,Material-UI 可以应用相同的建议修复,但我认为我们不应该这样做,它有两个缺点:1. 它只能与 Material-UI 一起使用,需要一遍又一遍地做同样的努力。 2. 这些“hacks”的所有权应该保持在 react-hook-form 中,因为它源于库采取的权衡(绕过惯用的 React API)。

嘿伙计们,万一有人介入这个问题。 (我们已经暗中研究了一段时间了😓)

我们正在为 RHF 开发下一个主要版本,它具有一些生活质量更新,尤其是在受控组件方面。 我们将对 UI 库有更好的支持。 以下代码将为受控 UI 库解析setValuedefaultValue ,同时仍然在您的应用程序/表单级别保持最少的重新渲染,重新渲染将在您的输入组件级别隔离。

以下将是您使用 RHF V4 时的语法。

import TextField from '@material-ui/core/TextField';

const { control } = useForm();

<Controller as={TextField} control={control} name="firstName" rules={{ required: true }} />

需要帮忙。
我遇到了材质文本字段的问题(也可能是其他 mui)。
当文本字段为空时,第一个密钥库很慢,当您将值更改为空时也是如此。
出于某种原因,它重新呈现整个表单。

Screen Shot 2020-02-11 at 3 21 25 AM

ezgif com-video-to-gif

快速说明:我打字速度很快,没有任何延迟。 他们延迟部分是由于上述原因。

嗨,伙计们,即使我使用 react-hook-form 中的控制器,这个问题仍然存在:(
这个问题可以重开吗? 还是我做错了?

示例: https :

编辑:我正在使用的解决方案是InputLabelProps={isEdition && { shrink: isEdition }}
其中isEdition是我在编辑屏幕上使用的标志。

@Luccasoli请参考文档: https : //react-hook-form.com/api#Controller

有一个带有 MUI 示例的示例: https :

抱歉@bluebill1049 ,但我在这个例子中没有发现任何类似的情况,并且在文档中我找不到任何可以正确解决这个问题的道具:(

Screen Shot 2020-04-15 at 9 21 56 am

@Luccasoli看看defaultValue

我试过:

  • 我尝试将 useForm() 中的 defaultValues 变成一个状态值,并在 useEffect() 中设置该状态
  • 我尝试使用相同的想法,但在 defaultValue 属性中直接每个控制器

两种方式,textField都无法识别输入已填充,标签保持在输入之上

是的,但奇怪的是保持这样的 defaultValue,我总是尝试从空字符串值开始,以免向用户显示奇怪的值。

我什至可以显示一个“正在加载”的默认值,但空的初始值似乎仍然更有趣,你认为这可能吗?

编辑:是的,也适用于空字符串......哈哈哈,谢谢,我发誓我以前试过这个,但似乎是这样。

@bluebill1049我调整了你的演示来展示我看到的问题,希望你有一个很好的解决方案。
https://codesandbox.io/s/react-hook-form-controller-n196b?file=/src/index.js
在启用按钮之前,尝试验证复选框已被选中。 除了复选框之外,我使用的所有其他字段都按预期工作。

我发现这个沙盒对于 Material UI 真的非常有用 - 有滑块、选择和其他一些示例。
https://codesandbox.io/s/react-hook-form-controller-079xx?file=/src/index.js

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

nathanmarks picture nathanmarks  ·  100评论

amcasey picture amcasey  ·  70评论

celiao picture celiao  ·  54评论

kybarg picture kybarg  ·  164评论

garygrubb picture garygrubb  ·  57评论