跳到主要内容

受控模式和非受控模式

改变表单的值只有两种情况,用户去改变或者代码去改变

受控模式

受控模式:代码可以改变表单的值 使用一般是:value+onChange

例如:

const App = () => {
const [value,setValue] = useState('')
return <input value={value} onChange={(e)=>{
console.log(e.target.value);
setValue(e.target.value)
}} />;
}

这种情况下就是受控模式,代码可以改变表单的值,用户也可以输入值, 但是每次set都会导致组件重新渲染。

非受控模式

非受控模式: 不能通过代码去改变,只能通过用户去改变,但是可以给表单设置初始值defaultValue,但是代码可以通过监听onChange事件或者通过ref拿到dom之后,获取到最新的值

使用一般是defaultValue+onChange/ref

注意,一但给表单input设置了value,那么用户就不能修改了,可以触发onChange事件,但是值不会变,但是你可以在onChange事件中,设置value的值,这样就可以实现受控模式

在下面的代码中,设置defaultValue,然后通过onChange事件,改变value的值,这种就是非受控模式

const App = () => {
const [value,setValue] = useState('')
console.log(value);

return <input defaultValue={'蔡徐坤'} onChange={(e)=>setValue(e.target.value)} />;
};

坏处就是,每次触发了onChange事件,都会去改变value的值,这样就会导致每次都会去重新渲染组件。

也可以通过ref拿到dom之后,获取到最新的值

const App = () => {
const inputRef = useRef<HTMLInputElement>(null)
console.log(inputRef.current?.value);

useEffect(()=>{
setTimeout(()=>{
console.log(inputRef.current?.value);
},1000)
},[])

return <input ref={inputRef} defaultValue={'蔡徐坤'} />;
}

同时支持受控模式和非受控模式

非受控模式:defalutValue+onChange

/**
* 理解受控组件和非受控组件
*/
interface CalenderProps{
defaultvalue?:Date
onChange?: (date:Date) => void
}

const Calender = (props:CalenderProps) => {
const {defaultvalue,onChange} = props
const [value,setValue] = useState(defaultvalue)
function changeValue(date:Date) {
setValue(date)
onChange?.(date)
}
return <div>
{value?.toLocaleDateString()}
<div onClick={()=>{changeValue(new Date('2025-02-01'))}}>2025-02-01</div>
<div onClick={()=>{changeValue(new Date('2025-02-02'))}}>2025-02-02</div>
<div onClick={()=>{changeValue(new Date('2025-02-03'))}}>2025-02-03</div>
</div>
}

const App = () => {
return <Calender defaultvalue={new Date('2025-02-01')} onChange={(date)=>{
console.log(date);
}} />
}

受控模式:value+onChange


/**
* 理解受控组件和非受控组件
*/
interface CalenderProps{
value?:Date
onChange?: (date:Date) => void
}

const Calender = (props:CalenderProps) => {
const {value,onChange} = props
function changeValue(date:Date) {
onChange?.(date)
}
return <div>
{value?.toLocaleDateString()}
<div onClick={()=>{changeValue(new Date('2025-02-01'))}}>2025-02-01</div>
<div onClick={()=>{changeValue(new Date('2025-02-02'))}}>2025-02-02</div>
<div onClick={()=>{changeValue(new Date('2025-02-03'))}}>2025-02-03</div>
</div>
}

const App = () => {
return <Calender value={new Date('2025-02-01')} onChange={(date)=>{
console.log(date);
}} />
}

同时支持受控和非受控



/**
* 理解受控组件和非受控组件
* defaultValue是 非受控
* value是 受控
*/
interface CalenderProps{
value?:Date
defaultvalue?:Date
onChange?: (date:Date) => void
}

const Calender = (props:CalenderProps) => {
const {value:propsValue,defaultvalue,onChange} = props
const [value,setValue] = useState(()=>{
if(propsValue!==undefined) {
return propsValue
}else {
return defaultvalue
}
})

const isFirstRender = useRef(true)

useEffect(()=>{
if(propsValue===undefined &&!isFirstRender.current) {
setValue(propsValue)
}
isFirstRender.current = false
},[propsValue])

const mergedValue = propsValue===undefined?value:propsValue

function changeValue(date:Date) {
if(propsValue===undefined) {
setValue(date)
}
onChange?.(date)
}
return <div>
{mergedValue?.toLocaleDateString()}
<div onClick={()=>{changeValue(new Date('2025-02-01'))}}>2025-02-01</div>
<div onClick={()=>{changeValue(new Date('2025-02-02'))}}>2025-02-02</div>
<div onClick={()=>{changeValue(new Date('2025-02-03'))}}>2025-02-03</div>
</div>
}

const App = () => {
return <Calender defaultvalue={new Date('2025-02-01')} onChange={(date)=>{
console.log(date);
}} />
}

自己封装一个受控和非受控的Hook


function useMergeState<T>(
defaultStateValue:T,
props?:{
defaultValue?:T,
value?:T
}
):[T,React.Dispatch<React.SetStateAction<T>>,]{
const {defaultValue,value:propsValue} =props||{}

const isFirstRender = useRef(true)

const [value,setValue] = useState<T>(()=>{
if(propsValue!==undefined) {
return propsValue
}if(defaultValue!==undefined) {
return defaultValue
}else {
return defaultStateValue
}
})

useEffect(()=>{
if(propsValue===undefined &&!isFirstRender.current) {
setValue(propsValue!)
}
isFirstRender.current = false
},[propsValue])
const mergedValue = propsValue===undefined?value:propsValue
return [mergedValue,setValue]
}

使用:


/**
* 理解受控组件和非受控组件
* defaultValue是 非受控
* value是 受控
*/
interface CalenderProps{
value?:Date
defaultValue?:Date
onChange?: (date:Date) => void
}

const Calender = (props:CalenderProps) => {

const {value:propsValue,defaultValue,onChange}=props
const [mergedValue,setValue]= useMergeState(new Date(),{
value: propsValue,
defaultValue
})

function changeValue(date:Date) {
if(propsValue===undefined){
setValue(date)
}
onChange?.(date)
}

return <div>
{mergedValue?.toLocaleDateString()}
<div onClick={()=>{changeValue(new Date('2025-02-01'))}}>2025-02-01</div>
<div onClick={()=>{changeValue(new Date('2025-02-02'))}}>2025-02-02</div>
<div onClick={()=>{changeValue(new Date('2025-02-03'))}}>2025-02-03</div>
</div>
}

参考资料

React通关秘籍