前言
了解或会 Vue
的朋友都知道,在 Vue
中我们可以通过 v-model
实现 受控组件的数据双向绑定,而在 React
中则需要通过 value
和 onChange
实现数据的双向绑定,单个还可以接受,如多个呢。
看个例子。
const [nickName, setNickName] = useState('')
const [age, setAge] = useState(null)
const handleNickNameChange = useCallback((value) => {
setNickName(value)
}, [])
const handleAgeChange = useCallback((value) => {
setAge(value)
}, [])
return (
<>
<Input value={nickName} onChange={handleNickNameChange} />
<Input value={age} onChange={handleAgeChange} />
</>
)
根据上面得出结论,如果一个组件内有多个受控组件,那将会向上面一样写很多个。我们能不能封装一下只需要声明变量,不需要声明 set
方法呢。答案是OK的,可以看下面。
Tips:input 的 type 的值为 file 时为非受控组件,原因是因为 type 为 file 时的 value 处于可读状态。
说明
我这里使用的是 React
+ ts
,使用js
的话需要删除变量后面的类型声明。
withModel
我这里封装的方法组件为 withModel
,你们可以根据自身命名。
//withModel.tsx
import React, { forwardRef, useMemo, useCallback, useEffect } from 'react'
// 双向绑定工具方法
const withModel = (Component: any) => forwardRef((props, outerRef) => {
const p = {
models: [],
name: '',
value: '',
onChange: (event: any) => {},
...props,
}
const { models = [], name, value, onChange, ...other } = p;
const [modelValue, setModelValue] = useMemo(() => models, [models])
const handleChange = useCallback((event: any) => {
if (setModelValue) {
const setValue = setModelValue as Function;
setValue(event.target.value)
}
if(typeof onChange === 'function') onChange(event)
}, [onChange])
return (
<Component
{...other}
ref={outerRef}
name={name}
value={modelValue !== undefined ? modelValue : value}
onChange={handleChange}
/>
)
})
export default withModel
input.tsx
//input.tsx
import React, { forwardRef } from "react";
import withModel from '../utils/withModel'
type inputProps = React.DetailedHTMLProps<
React.InputHTMLAttributes<HTMLInputElement>,
HTMLInputElement
>
const Component = forwardRef<HTMLInputElement, inputProps>((props, outerRef) => {
const p = {...props};
let { type } = props;
if(!type) type = 'text';
let element = <input ref={outerRef} {...props} />
return (
<>
{!['checkbox', 'file', 'radio'].includes(type) && element}
</>
)
})
Component.displayName = 'Input'
export default withModel(Component );
页面使用
// 页面使用
import React, { useState } from "react";
import Input from "./Input"
export default () => {
const data = useState('我是输入的内容')
return (
<>
<Input models={data} placeholder="请输入内容" />
{/* */}
<p>{`我是输入的内容: ${data[0]}`}</p>
</>
)
}
总结
原理:使用 forwardRef
将当前受控组件的 ref
引用进行传递,通过 withModel
组件方法进行修改。
以上就是 基于 React
的 Hook
实现受控组件双向绑定的一个过程。
如有不足还请各位指点。
搞定收工。