简介
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
),如果传递了,就作为受控组件处理;如果没有传递,就作为非受控组件处理。
通过以下步骤来实现这一点:
- 检查是否传递了控制属性。
- 如果传递了控制属性,就使用该属性作为组件的值。
- 如果没有传递控制属性,就使用组件内部的状态来管理值。
示例代码
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
控制。通常需要value
和onChange
属性。 - 非受控组件 :状态由组件内部的
state
控制,通常使用ref
来访问 DOM 元素的值。
在开发一个React组件时,可以创建一个灵活的组件,能够在受控和非受控两种模式下工作。这样可以在不同的使用场景中提供更大的灵活性。
理解受控和非受控组件的区别有助于你在设计和使用 React 组件时做出更好的选择。希望这些示例和解释对你有所帮助!