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 组件就是这样的:

相关推荐
黑云压城After2 小时前
纯css实现加载动画
服务器·前端·css
鹏多多2 小时前
Web使用natapp进行内网穿透和预览本地页面
前端·javascript
ttod_qzstudio2 小时前
Vue 3 Props 定义详解:从基础到进阶
前端·vue.js
钱端工程师2 小时前
uniapp封装uni.request请求,实现重复接口请求中断上次请求(防抖)
前端·javascript·uni-app
dcloud_jibinbin2 小时前
【uniapp】解决小程序分包下的json文件编译后生成到主包的问题
前端·性能优化·微信小程序·uni-app·vue·json
茶憶2 小时前
uniapp移动端实现触摸滑动功能:上下滑动展开收起内容,左右滑动删除列表
前端·javascript·vue.js·uni-app
Ayn慢慢2 小时前
uni-app PDA焦点录入实现
前端·javascript·uni-app
一位搞嵌入式的 genius2 小时前
微前端架构:JavaScript 隔离方案全解析(含 CSS 隔离)概要
前端·css·前端实战
4_0_42 小时前
一步一步实现 Shader 水波纹效果(入门到进阶)
前端·three.js