当前位置: 首页 > article >正文

React 中的受控组件与非受控组件:深度剖析与实战应用

一、引言

在如今的前端开发里,React 可是响当当的角色,是咱搭建用户界面的得力帮手。一碰到表单处理和用户输入交互这些事儿,受控组件和非受控组件就派上大用场了,它们就像是两种不一样的工具,各有各的厉害之处。要是咱能把它们摸透了,知道啥时候用啥,那开发出来的 React 应用肯定既好用又靠谱,用户体验也差不了。

二、受控组件

(一)定义咋来的

受控组件,简单讲,就是让表单元素的值跟 React 组件的state绑得死死的,完全同步。用户在表单上不管是打字还是选东西,都像触动了个开关,马上让组件状态更新,接着组件就重新渲染一遍,保证表单显示的值和组件里存的值一模一样,一点差错都没有。这其实就是 React 响应式编程的一种典型做法,用状态这个 “老大” 管着表单元素,让一切都井井有条。

(二)啥时候用它

  1. 需要实时查错、给反馈的时候 :像用户注册、登录,还有提交各种信息的表单,对数据准不准要求可高了。就说电商平台的注册表单吧,用户填电子邮箱,刚输完,组件就能立马用正则表达式瞅瞅格式对不对。要是不对,马上在输入框旁边亮个红灯,给个错误提示,还能拦住表单不让提交。这靠的就是受控组件和状态的紧密配合,在onChange事件回调里,实时更新状态,再根据新状态决定显示啥反馈,这么一来,用户就能及时改错误,表单填得又快又准。
  2. 要让界面联动起来的时候 :想象一下在线文档编辑工具,用户调字体大小,文本输入框里的字得跟着变吧,不光如此,标题栏字体、预览区字体,还有排版样式,好多相关的 UI 元素都得一起变。受控组件在这儿就起大作用了,把字体大小这些关键信息放在组件状态里管着。onChange事件一触发,就统一更新状态、重新渲染 UI,各个相关部分就像配合默契的齿轮,一起转起来,给用户的操作提供顺滑流畅的反馈。

(三)代码咋写的,举俩例子

 
先看个多功能文本输入框的例子:

import React, { useState } from'react';

function ControlledInput() {
    const [inputValue, setInputValue] = useState('');
    const [isFocused, setIsFocused] = useState(false);
    const [charCount, setCharCount] = useState(0);

    const handleChange = (e) => {
        const newValue = e.target.value;
        setInputValue(newValue);
        setCharCount(newValue.length);
    };

    const handleFocus = () => {
        setIsFocused(true);
    };

    const handleBlur = () => {
        setIsFocused(false);
    };

    return (
        <div>
            <input
                type="text"
                value={inputValue}
                onChange={handleChange}
                onFocus={handleFocus}
                onBlur={handleBlur}
            />
            <p>Character count: {charCount}</p>
            {isFocused? (
                <span style={{ color: 'blue', fontWeight: 'bold' }}>Input is focused</span>
            ) : null}
        </div>
    );
}

export default ControlledInput;

在这个例子里,文本输入框的value属性就像和组件的inputValue状态锁在一起了,不离不弃。用户一输入,onChange事件立马响应,handleChange函数上场,不光更新inputValue,让输入框显示最新内容,还顺便算出字符个数,更新charCount状态,在界面上实时显示字符统计。同时,onFocus和onBlur事件分别管着isFocused状态,输入框聚焦就显示个蓝字提示,失焦就不显示,通过巧妙地操控状态,把输入框的各种行为和界面反馈安排得明明白白。

再看个更复杂的,模拟在线笔记编辑,有表单验证和自动保存功能:

import React, { useState, useEffect } from'react';

function NoteEditor() {
    const [noteContent, setNoteContent] = useState('');
    const [isValidNote, setIsValidNote] = useState(true);
    const [isSaving, setIsSaving] = useState(false);
    const [lastSavedTime, setLastSavedTime] = useState(null);

    const handleNoteChange = (e) => {
        const newContent = e.target.value;
        setNoteContent(newContent);
        const noteRegex = /^.{1,100}$/;
        setIsValidNote(noteRegex.test(newContent));
    };

    useEffect(() => {
        if (isValidNote && noteContent) {
            const interval = setInterval(() => {
                setIsSaving(true);
                // 模拟保存操作,实际可能是API调用
                setTimeout(() => {
                    setIsSaving(false);
                    setLastSavedTime(Date.now());
                }, 2000);
            }, 5000);
            return () => clearInterval(interval);
        }
    }, [isValidNote, noteContent]);

    return (
        <div>
            <textarea
                value={noteContent}
                onChange={handleNoteChange}
            />
            {isValidNote? null : (
                <span style={{ color:'red' }}>Note must be between 1 and 100 characters</span>
            )}
            {isSaving? (
                <span style={{ color: 'orange', fontWeight: 'bold' }}>Saving...</span>
            ) : null}
            <p>Last saved: {lastSavedTime? new Date(lastSavedTime).toLocaleString() : 'Never'}</p>
        </div>
    );
}

export default NoteEditor;

这里,textarea是核心表单元素,它的值和noteContent状态绑得紧紧的。onChange回调不光更新内容、实时查错,还根据结果决定错误提示的显示。同时,用useEffect钩子监听内容合法性和有没有值,条件满足就启动定时自动保存,模拟真实应用里的数据保存操作,保存过程中切换状态显示保存状态,最后记录并显示上次保存时间,全方位展示了受控组件在复杂业务场景下的本事。

三、非受控组件

(一)定义是啥意思

非受控组件呢,就像是个比较随性的家伙,表单元素的值由 DOM 自己管着,React 组件平时不插手,就等关键时候,用ref这个 “工具” 去拿 DOM 元素的值。它不折腾那些复杂的状态同步,尊重 DOM 本来的样子,和表单元素打交道简单直接,就像是给咱开了条捷径。

(二)适合啥情况

  1. 集成第三方表单的时候 :现在前端的工具、库特别多,有些第三方表单库功能超强。要是引入react-bootstrap表单组件库里的高级日期选择器,硬要把它改成受控组件,那麻烦可就大了,不光可能碰到兼容性问题,代码还会变得超级复杂。这时候非受控组件就好用了,用ref轻松拿到日期选择器选的值,顺顺利利就把它集成到 React 项目里,就像搭了座桥,让不同的东西能一起干活。
  2. 处理文件上传的时候 :文件上传这事儿有点特殊,因为浏览器的安全策略,还有文件对象本身的性质,受控组件不好使。就说社交媒体应用里的图片上传功能,用户选了本地照片,文件输入框里的文件对象不能像普通文本一样让 React 状态随便改,更不能直接设置它的值。非受控组件就靠ref精准找到文件输入框,提交的时候一把抓住文件对象,后面就能把文件上传到服务器,这就把文件上传的难题给解决了。

(三)代码咋弄,看俩例子

先看个简单的非受控组件文本输入框:

import React, { useRef } from'react';

function UncontrolledInput() {
    const inputRef = useRef(null);
    const [submittedValue, setSubmittedValue] = useState('');

    const handleSubmit = (e) => {
        e.preventDefault();
        const inputValue = inputRef.current.value;
        setSubmittedValue(inputValue);
        console.log('You entered:', inputValue);
    };

    return (
        <form onSubmit={handleSubmit}>
            <input
                type="text"
                ref={inputRef}
            />
            <button type="submit">Submit</button>
            <p>Submitted: {submittedValue}</p>
        </form>
    );
}

export default UncontrolledInput;

在这个例子里,useRef搞出来的inputRef就像一根线,把 React 组件和 DOM 里的文本输入框连上了。平时用户输入,输入框的值在 DOM 里自己变,React 不管。等提交按钮一点,handleSubmit函数靠inputRef.current.value把 DOM 里的值拿过来,放到submittedValue状态里存着,还显示出来,就这么实现了从 DOM 到 React 的过渡。

再看个进阶的多文件上传例子:

import React, { useRef, useState } from'react';

function MultiFileUpload() {
    const fileInputRef = useRef(null);
    const [fileList, setFileList] = useState([]);

    const handleUpload = (e) => {
        e.preventDefault();
        const files = fileInputRef.current.files;
        if (files) {
            const fileArray = Array.from(files);
            setFileList(fileArray);
            console.log('Uploading files:', fileArray.map((file) => file.name));
            // 后续可添加文件上传至服务器的逻辑
        }
    };

    return (
        <form onSubmit={handleUpload}>
            <input
                type="file"
                multiple
                ref={fileInputRef}
            />
            <button type="submit">Upload</button>
            <ul>
                {fileList.map((file, index) => (
                    <li key={index}>{file.name}</li>
                ))}
            </ul>
        </form>
    );
}

export default MultiFileUpload;

这个多文件上传的例子里,fileInputRef紧紧跟着文件输入框。用户批量选文件再提交的时候,handleUpload函数靠fileInputRef.current.files拿到文件,转成数组存到fileList状态里,实时显示要上传的文件列表,既尊重了文件输入框 DOM 的原生行为,又把它融进了 React 的数据管理,给文件上传流程搭了个好框架。

四、受控组件与非受控组件的对比

(一)数据咋流动的

  • 受控组件:就像建了个双向高速路,数据从用户操作开始,流到组件状态里存着,然后 React 根据状态更新表单元素,又反馈给用户,形成一个闭环,实时响应。每次交互都是状态和视图一起变,保证用户看到的和操作的完全同步。
  • 非受控组件:数据走的是单向路,大多时候在 DOM 里自己流转,表单元素自己管自己的值。React 组件就偶尔用ref去 DOM 那儿取个值,像是偶尔采个蜜的蜜蜂,和 DOM 是种松散关系。

(二)代码复杂不复杂

  • 受控组件:因为要精细维护状态,调度各种复杂逻辑,代码结构就像个精密仪器,一环扣一环。从一开始设状态,到onChange、onBlur等好多事件回调里更新状态,再到根据不同状态显示不同 UI 组件,每个环节都得精心弄。特别是处理多个表单元素联动、深度验证的时候,状态树变得老复杂了,虽然掌控力强,但调试、维护起来不容易,得技术好才能驾驭。
  • 非受控组件:代码风格简单直接,不用搭复杂的状态体系,直接用ref和 DOM 交流,少了中间那些逻辑层。不过在大项目里,要是老用ref在 DOM 和 React 之间穿梭,代码就像散了架,逻辑不连贯,维护起来也麻烦,得看情况用,别给自己挖坑。

(三)啥时候用哪个好

  • 受控组件:要是追求极致的交互体验,对数据管控要求特别精细,那就选它。像金融产品风险评估、医疗信息录入这些重要又复杂的表单,数据准不准至关重要,实时反馈、联动调整都不能少。受控组件靠状态驱动,把用户输入、验证规则、UI 显示绑得紧紧的,虽然复杂,但可靠,能给关键业务护航。
  • 非受控组件:要是碰到特殊情况,像集成第三方表单有兼容性问题,或者处理文件上传这种受限的事儿,它就好使。它不纠结 React 状态,拥抱 DOM 原生力量,用最小代价把功能实现,给项目推进和满足多样需求提供灵活办法。

五、总结

在 React 开发里,受控组件和非受控组件就像一对好搭档,各有千秋,互相补充。咱开发者得从项目全局考虑,看业务需求是啥样,团队技术水平咋样,再决定用哪个。把它们的原理、用法、适用场景都搞清楚了,开发表单和用户输入交互功能的时候,就能得心应手。复杂业务咱能处理得稳稳当当,特殊场景也能轻松应对。

作为程序员,持续学习和充电非常重要,作为开发者,我们需要保持好奇心和学习热情,不断探索新的技术,只有这样,我们才能在这个快速发展的时代中立于不败之地。低代码也是一个值得我们深入探索的领域,让我们拭目以待,它将给前端世界带来怎样的变革,推荐一个低代码工具。

应用地址: https://www.jnpfsoft.com
开发语言:Java/.net

这是一个基于Flowable引擎(支持java、.NET),已支持MySQL、SqlServer、Oracle、PostgreSQL、DM(达梦)、 KingbaseES(人大金仓)6个数据库,支持私有化部署,前后端封装了上千个常用类,方便扩展,框架集成了表单、报表、图表、大屏等各种常用的 Demo 方便直接使用。


http://www.kler.cn/a/467047.html

相关文章:

  • vite6+vue3+ts+prettier+eslint9配置前端项目(后台管理系统、移动端H5项目通用配置)
  • 【sql】CAST(GROUP_CONCAT())实现一对多对象json输出
  • Go小技巧易错点100例(二十一)
  • 力扣66 加一
  • ThreadPoolExecutor keepAliveTime 含义
  • 如何恢复已删除的 Telegram 消息 [iOSamp;Android]
  • 微服务拆分的艺术:构建高效、灵活的系统架构
  • 清华发布Hyper-YOLO:超图计算+目标检测!捕捉高阶视觉关联
  • spring默认线程池SimpleAsyncTaskExecutor特点为什么要尽量避免使用
  • Java四大常用JSON解析性能对比:Hutool、Fastjson2、Gson与Jackson测试
  • Nginx:日志管理
  • 零基础WPF使用NLog记录日志
  • CPU与GPU的区别
  • C/C++中 <<与<<=的介绍和区别
  • Ungoogled Chromium127 编译指南 MacOS 篇(一)- 项目介绍
  • 【Leetcode 热题 100】74. 搜索二维矩阵
  • 【2025最新计算机毕业设计】基于Spring Boot+Vue影院购票系统(高质量源码,提供文档,免费部署到本地)
  • Python 开发框架搭建简单博客系统:代码实践与应用
  • Edge安装问题,安装后出现:Could not find Edge installation
  • 30分钟学会css
  • 电商Google广告:2025年提升转化率的5种策略
  • 八字算命网站搭建方法:从零开始用php搭建一个命理网
  • 才气小波与第一性原理
  • [gcc]常见编译开关
  • 使用GitLab+Jenkins搭建CICD执行环境
  • 滴滴工作流引擎Turbo与logicFlow研究