浅谈 React组件设计之受控和非受控组件

简介

React 中的受控和非受控是非常重要的知识吧。当我们在使用一些组件库的时候,比如Antd,是否也关注到了这些库中的组件即支持受控也同样支持非受控呢?

不错,抛开组件库,当我们在开发一些组件时,如果此组件即支持受控也同样支持非受控,那么对于组件的使用者来说,能提高组件更大的灵活性和扩展性以及可复用性。

受控组件:由 prop 驱动

非受控组件:由自定义组件内部的 state 驱动

最后,本文将开发一个示例组件,且同时支持受控和非受控两种模式

受控组件

受控组件是指其状态完全由外部通过 props 控制的组件。通常,这些组件会接收一个值(如 value )和一个事件处理函数(如 onChange),并且组件内部不会维护自己的状态。

示例

一个简单的受控输入框组件:

js 复制代码
import React from 'react';

const ControlledInput = ({ value, onChange }) => {
  return (
    <input type="text" value={value} onChange={onChange} />
  );
};

export default ControlledInput;

使用这个受控组件:

js 复制代码
import React, { useState } from 'react';
import ControlledInput from './ControlledInput';

const App = () => {
  const [inputValue, setInputValue] = useState('');

  const handleChange = (event) => {
    setInputValue(event.target.value);
  };

  return (
    <div>
      <ControlledInput value={inputValue} onChange={handleChange} />
      <p>Current Value: {inputValue}</p>
    </div>
  );
};

export default App;

在这个例子中,ControlledInput 组件的值完全由 App 组件的状态 inputValue 控制。

非受控组件

非受控组件是指其状态由组件内部的 state 控制,外部不直接控制其状态。通常,这些组件会使用 ref 来访问 DOM 元素的值。

示例

一个简单的非受控输入框组件:

js 复制代码
import React, { useRef } from 'react';

const UncontrolledInput = () => {
  const inputRef = useRef(null);

  const handleClick = () => {
    alert(`Current Value: ${inputRef.current.value}`);
  };

  return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={handleClick}>Show Value</button>
    </div>
  );
};

export default UncontrolledInput;

使用这个非受控组件:

js 复制代码
import React from 'react';
import UncontrolledInput from './UncontrolledInput';

const App = () => {
  return (<UncontrolledInput />);
};

export default App;

在这个例子中,UncontrolledInput 组件的值由组件内部的 ref 控制,外部组件无法直接控制它。

组件同时支持受控和非受控两种模式

可以在组件中检查是否传递了控制属性(例如 value),如果传递了,就作为受控组件处理;如果没有传递,就作为非受控组件处理。

通过以下步骤来实现这一点:

  1. 检查是否传递了控制属性。
  2. 如果传递了控制属性,就使用该属性作为组件的值。
  3. 如果没有传递控制属性,就使用组件内部的状态来管理值。

示例代码

js 复制代码
import React, { useState, useRef, useEffect } from 'react';

const HybridInput = ({ value, defaultValue, onChange }) => {
  // 内部状态用于非受控模式
  const [internalValue, setInternalValue] = useState(defaultValue || '');
  // 受控模式检查
  const isControlled = value !== undefined;

  // 创建一个 ref 用于非受控模式
  const inputRef = useRef(null);

  // 如果组件处于受控模式,使用外部传递的 `value`;否则,使用内部状态 `internalValue`
  const inputValue = isControlled ? value : internalValue;

  // 处理输入变化
  const handleChange = (event) => {
    // 如果是非受控模式,更新内部状态 
    if (!isControlled) {
       setInternalValue(event.target.value);
    }
    
    // 受控和非受控,只要有onChange事件就执行,利于组件使用方使用
    onChange?.(event);
    }
  };

  // 同步 defaultValue 到内部状态
  useEffect(() => {
    if (!isControlled && defaultValue !== undefined) {
      setInternalValue(defaultValue);
    }
  }, [defaultValue, isControlled]);

  return (
    <input
      type="text"
      value={inputValue}
      onChange={handleChange}
      ref={inputRef}
    />
  );
};

export default HybridInput;

使用示例

受控模式

js 复制代码
import React, { useState } from 'react';
import HybridInput from './HybridInput';

const ControlledExample = () => {
  const [value, setValue] = useState('');

  const handleChange = (event) => {
    setValue(event.target.value);
  };

  return (
    <div>
      <HybridInput value={value} onChange={handleChange} />
      <p>Current Value: {value}</p>
    </div>
  );
};

export default ControlledExample;

非受控模式

js 复制代码
import React from 'react';
import HybridInput from './HybridInput';

const UncontrolledExample = () => {
  return (
    <div>
      <HybridInput defaultValue="Initial Value" />
    </div>
  );
};

export default UncontrolledExample;

总结

  • 受控组件 :状态由外部通过 props 控制。通常需要 valueonChange 属性。
  • 非受控组件 :状态由组件内部的 state 控制,通常使用 ref 来访问 DOM 元素的值。

在开发一个React组件时,可以创建一个灵活的组件,能够在受控和非受控两种模式下工作。这样可以在不同的使用场景中提供更大的灵活性。

理解受控和非受控组件的区别有助于你在设计和使用 React 组件时做出更好的选择。希望这些示例和解释对你有所帮助!

相关推荐
互联网搬砖老肖12 分钟前
React组件(一):生命周期
前端·javascript·react.js
我科绝伦(Huanhuan Zhou)18 分钟前
深入解析Shell脚本编程:从基础到实战的全面指南
前端·chrome
小马哥编程19 分钟前
React和Vue在前端开发中, 通常选择哪一个
前端·vue.js·react.js
aklry21 分钟前
uniapp实现在线pdf预览以及下载
前端·pdf·uni-app
HCl+NaOH=NaCl+H_2O40 分钟前
Quasar组件 Carousel走马灯
javascript·vue.js·ecmascript
℘团子এ44 分钟前
vue3中预览Excel文件
前端·javascript
shmily麻瓜小菜鸡1 小时前
在 Angular 中, `if...else if...else`
前端·javascript·angular.js
bloglin999992 小时前
npm和nvm和nrm有什么区别
前端·npm·node.js
2501_910227542 小时前
web3 前端常见错误类型以及错误捕获处理
前端·web3
哎哟喂_!3 小时前
Node.js 同步加载问题详解:原理、危害与优化策略
前端·chrome·node.js