组件自带数据管理
傻瓜化组件,不写代码,组件也能进行表单交互。这样开发是不是很 nice 🎉。
hooks简单就能实现,听我娓娓道来。
🗒️ 组件数据受控与非受控
不需要通过数据传入,组件就拥有自己的数据。这压根就是 非受控组件嘛,是的 🙃🙃🙃
组件代码大概长这样:
// components/test.jsx
import React, { useState } from 'react'
import { Button } from 'antd'
export default () => {
const [state, setState] = useState(0)
return (
<div>
<p>{state}</p>
<Button
onClick={() => {
setState(state + 1)
}}
>
点击加一
</Button>
</div>
)
}
使用非受控组件,啥也不用传,组件自己自带数据,能自己和用户交互。
import React from 'react'
import TT from 'components/test'
export default props => {
return <Test />
}
受控组件尴尬的点就是,组件外没办法操作组件的数据,也没办法获取数据,组件数据只能自生自灭。那如果改成受控组件呢,那代码可能就长这样了。
import React, { useState } from 'react'
import { Button } from 'antd'
export default props => {
const { state, setState } = props
return (
<>
<p>{state}</p>
<Button
onClick={() => {
setState(state + 1)
}}
>
点击加一
</Button>
</>
)
}
使用受控组件,那就需要自己在父组件做数据状态管理了。
import React from 'react'
import TT from 'components/test'
export default props => {
const [state, setState] = useState(0)
return <Test state={state} setState={setState} />
}
那到底是用受控组件还是非受控组件呢,使用非受控组件,对数据没办法获取、干预组件数据。用受控组件呢,父组件还得自己维护数据管理,麻烦。还离题了 👎👎,扯犊子的组件自带数据管理 💣💣💣。那么本文就是为了给出完美答案 👏👏,让组件既能受控,又不需要父组件管理数据。
组件交互设计
目的就是组件不需要做数据管理,但又能操作数据。先参考一波 antd4.0 表单的交互设计。
思路:通过 Form.useForm 对表单数据域进行交互。
import React from 'react'
import { Form, Button } from 'antd'
export default () => {
const [form] = Form.useForm()
return (
<div>
<Form form={form}>
<Form.Item name="note" label="Note">
<Input />
</Form.Item>
</Form>
<Button
type="primary"
onClick={() => {
message.info(form.getFieldsValue('note'))
}}
>
查看数据
</Button>
<Button
type="primary"
onClick={() => {
form.setFieldsValue({
note: 'Hello world!'
})
}}
>
修改数据
</Button>
</div>
)
}
**分析一波:**Form 里面自带数据管理,类似上面的受控组件。外层(父级组件)想操作组件数据,则通过 Form.useForm()实现,如getFieldsValue、setFieldsValue。
设计实现
一份分析猛如虎,antd4 表单数据交互是基于rc-form实现的,那如果让我们写一个中台组件,我们怎么实现这个逻辑呢,小组件总不能引一个大包吧。当然是自己动手写,就几行代码就搞定了。来人,上代码 🏇🏇。
先介绍怎么使用,懂得用才容易懂得实现,假设组件EditableTags 就是我们要开发的中台组件,功能是对 tag 标签的增删。
import React from 'react'
import EditableTags,{ useTags } from './tags'
import { Button } from 'antd'
export default () => {
let tagsHook = useTags()
return (
<div>
<EditableTags
defaultValue={[
'第一',
'第二',
'第三',
]}
tagsHook={tagsHook} //用于获取数据,当然也可以用onChange获取
/>
<Button
onClick={() => {
console.log("tags", tagsHook.getTags())
}}
>
查看
</Button>
</div>
)
}
EditableTags 组件部分代码
**_store:**存储组件数据,所有组件的数据都存在这个 store
tagsHook: 外层 useTags 传进来的函数,用法同 antd4 form
import { tagsInit, _store } from './store'
export default () => {
const [tags, setTags, index] = tagsInit(defaultValue)
tagsHook && tagsHook(index)
return <div>...</div>
}
./store 的代码
核心部分是./store 的代码。
_store
**解析:**返回一个函数,形成闭包。主要是为了保留这个 index 索引,确保每个组件都有自己唯一索引,_store 是所有组件的数据。
function useTags() {
let index = null // 组件调用时候 会进行赋值
let func = i => {
index = i
}
func.getTags = () => {
if (index === null) {
throw new Error('your must set useTags to a Tags components property ')
}
return _store[index]
}
func.setTags = data => {
if (index === null) {
throw new Error('your must set useTags to a Tags components property ')
}
_store[index] = data
}
return func
}
tagsInit
每个组件数据初始化,则是通过 tagsInit
import { useEffect, useState, useRef } from 'react'
const _store = {}
let _length = 0
function tagsInit(defaultValue) {
let index = useRef(_length++).current
const [data, setData] = useState(_store[index] || defaultValue || [])
const setTags = data => {
setData(data)
_store[index] = data
}
useEffect(() => {
_store[index] = data
//删除该元素 释放内存
return () => {
delete _store[index]
}
}, [])
return [data, setTags, index]
}
是不是很简单。tagsInit+_store 合起来就是store文件了
✨ 最后
写好中台组件交互,后端能也得心应手开发前端业务。设计好中台组件交互很关键哦。 字节跳动急召:工资高,福利顶。欢迎简历投递:xiaoyanhui@bytedance.com