浅谈 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 组件时做出更好的选择。希望这些示例和解释对你有所帮助!

相关推荐
程序猴老王2 分钟前
el-select 和el-tree二次封装
前端·vue.js·elementui
blzlh13 分钟前
手把手教你做网易云H5页面,进大厂后干的第一件事
前端·javascript·css
贩卖纯净水.40 分钟前
网站部署及CSS剩余模块
前端·css
Justinc.1 小时前
CSS3_BFC(十二)
前端·css·css3
梅子酱~1 小时前
Vue 学习随笔系列十七 -- 表格样式修改
javascript·vue.js·学习
刺客-Andy1 小时前
React第四节 组件的三大属性之state
前端·javascript·react.js
黄毛火烧雪下1 小时前
React 表单Form 中的 useWatch
前端·javascript·react.js
爱健身的小刘同学2 小时前
钉钉免登录接口
前端·javascript·钉钉
啵咿傲2 小时前
跨域相关的一些问题 ✅
前端
命运之光2 小时前
生日主题的烟花特效HTML,CSS,JS
前端·javascript·css