react学习6:受控组件

本文来自于光神的文章,搬到这里方便自己复习学习,没其他作用。

前端开发经常会涉及表单的处理,或者其他一些用于输入的组件。涉及到输入,就绕不开受控模式和非受控模式的概念。

什么是受控,什么是非受控呢?想一下,改变表单值只有两种情况:

用户去改变 value 或者代码去改变 value。

如果不能通过代码改表单值 value,那就是非受控,也就是不受我们控制。但是代码可以给表单设置初始值 defaultValue。

代码设置表单的初始 value,但是能改变 value 的只有用户,代码通过监听 onChange 来拿到最新的值,或者通过 ref 拿到 dom 之后读取 value。

这种就是非受控模式。

反过来,代码可以改变表单的 value,就是受控模式。

注意,value 和 defaultValue 不一样:

defaultValue 会作为 value 的初始值,后面用户改变的是 value。

而一旦你给 input 设置了 value,那用户就不能修改它了,可以输入触发 onChange 事件,但是表单的值不会变。

用户输入之后在 onChange 事件里拿到输入,然后通过代码去设置 value。

这就是受控模式。

其实绝大多数情况下,非受控就可以了,因为我们只是要拿到用户的输入,不需要手动去修改表单值。

但有的时候,你需要根据用户的输入做一些处理,然后设置为表单的值,这种就需要受控模式。

或者你想同步表单的值到另一个地方的时候,类似 Form 组件,也可以用受控模式。

value 由用户控制就是非受控模式,由代码控制就是受控模式。

下面看一下非受控组件:

js 复制代码
const [checked, setChecked] = useState(true)
<div>
  <label htmlFor="checkbox">checkbox</label>
  <input
    type="checkbox"
    id="checkbox"
    value="beijing"
    defaultChecked={checked}
    onChange={(e: ChangeEvent<HTMLInputElement>) => {
      console.log('e.target.checked', e.target.checked)
      console.log('e.target.value', e.target.value)
    }}
  />
</div>

此时,我只是设置默认值defaultChecked,所以如果我要获取这个选中状态,需要通过onChange函数获取。

如果我加上checked={checked},不加setChecked(e.target.checked)的话:

js 复制代码
<input
    type="checkbox"
    id="checkbox"
    value="beijing"
    checked={checked}
    onChange={(e: ChangeEvent<HTMLInputElement>) => {
      console.log('e.target.checked', e.target.checked)
      console.log('e.target.value', e.target.value)
    }}
    />

此时,点击checkbox不会有任何变化,必须要使用setChecked(e.target.checked)进行更新。

这就是受控组件。

虽然功能上差不多,但这种写法并不推荐:

你不让用户自己控制,而是通过代码控制,绕了一圈结果也没改 value 的值,还是原封不动的,图啥呢?

而且受控模式每次 setValue 都会导致组件重新渲染。试一下:

每次输入都会 setValue,然后触发组件重新渲染,而非受控模式下只会渲染一次。

绕了一圈啥也没改,还导致很多组件的重新渲染,那你用受控模式图啥呢?

那什么情况用受控模式呢?

当然是你需要对输入的值做处理之后设置到表单的时候,或者是你想实时同步状态值到父组件。

比如把用户输入改为大写:

js 复制代码
import { ChangeEvent, useState } from "react"

function App() {

  const [value, setValue] = useState('guang');

  function onChange(event: ChangeEvent<HTMLInputElement>) {
    console.log(event.target.value)
    setValue(event.target.value.toUpperCase());
  }

  return <input value={value} onChange={onChange}/>
}

export default App

这种,需要把用户的输入修改一下再设置 value 的。

但这种场景其实很少。

有的同学可能会说 Form 组件,确实,用 Form.Item 包裹的表单项都是受控组件:

确实,那是因为 Form 组件内有一个 Store,它需要把表单值同步到store里面,然后集中管理和设置值,比如有个getFieldsValue函数获取所有表单的值。

但也因为都是受控组件,随着用户的输入,表单重新渲染很多次,性能会不好。

如果是单独用的组件,比如 Calendar,那就没必要用受控模式了,用非受控模式,设置 defaultValue 就可以了。

很多人上来就设置 value,然后监听 onChange,但是绕了一圈又原封不动的把用户输入转为 value。

没啥意义,还平白导致组件的很多次重新渲染。

除了原生表单元素外,组件也需要考虑受控和非受控的情况。

比如日历组件:

它的参数就要考虑是支持非受控模式的 defaultValue,还是用受控模式的 value + onChange。

如果这是一个业务组件,那基本就是用非受控模式的 defaultValue 了,调用方只要拿到用户的输入就行。

用受控模式的 value 还要 setValue 触发额外的渲染。

但是基础组件不能这样,你得都支持,让调用者自己去选择。

ant design 的 Calendar 组件就是这样的:

相关推荐
粥里有勺糖10 分钟前
安利一下最近用的桌面“Agent” | T3 Code
前端·github·ai编程
xy345316 分钟前
Axure 9.0 动态面板基本操作
前端·ui·html·axure·原型·产品设计
不停喝水25 分钟前
【前端转全栈java速通课】 项目实战④7-11节 操作数据库-登录-注册-修改密码-注销-mubatis-plus简化crud-
java·前端·数据库
一水鉴天1 小时前
统一对象拓扑(UOT)设计全链定稿:从三件事分离到三继承 之1 20261010(豆包“查看聊天内容”继续)
前端·css
栖凤1 小时前
Java 的 try-catch 在 Agent 里失效了,我用了 5 个模式才兜住
java·前端·python
粥里有勺糖1 小时前
视野修炼第136期 | 前端小恐龙"Deno"被收购
前端·github·ai编程
修炼的dance2 小时前
页面塞了13.88 MB字体,导出的PDF只有71 KB
前端
qq_369173632 小时前
如何将 AI 生成的 HTML 网页发布成在线链接?不用自己搭建服务器
前端·人工智能·html·效率工具·html 发布
风花一世月2 小时前
⚡ 我把一座含氢能源园区搬进了浏览器(五类能流实时算守恒,在线直接玩)
前端·人工智能
羲云2 小时前
Claude 越界事件之后:Agent 权限设计为什么不能只靠提示词
前端