前言
了解或会 Vue
的朋友都知道,在 Vue
中我们可以通过 v-model
实现 受控组件的数据双向绑定,而在 React
中则需要通过 value
和 onChange
实现数据的双向绑定,单个还可以接受,如多个呢。
看个例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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
,你们可以根据自身命名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
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
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
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 );
|
页面使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
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
组件方法进行修改。
搞定收工。