Vue和React hooks实现Select批量选择、选中条数、全选、反选实现对比
2014 年 2 月 22 日
批量选择、全选、反选这些功能使用频率还是很高的。
Vue
在使用Vue的时候是很容易实现的,我们以下列数据格式为例:
const dataList = [{ id: 1, label: '第一个', }, { id: 2, label: '第二个', }, { id: 3, label: '第三个', }]
批量选择在Vue中我们一般喜欢格外增加一个属性来标记是否已被选择,可以在每条数据item被勾选时,比如第一条的话就是 Vue.set(dataList[0], 'checked', !!dataList[0].checked)
; 选中条数
const count = dataList.filter(item => item.checked).length;
全选
dataList.forEach(item => Vue.set(item, 'checked', true));
反选
dataList.forEach(item => Vue.set(item, 'checked', false));
React实现一
在React中怎么实现呢?
如果只是简单按照同样的方式的话,你就会失望了。
因为React不是跟Vue采用观察者模式来处理数据更新后的视图变更逻辑的,它更多是需要进行全量更新数据的。
对应数组而言,是引用类型数据,尽管里面某个元素发生了变更,但是对于整体数组的引用地址并未变更,React也就无法进行视图更新。
我们以使用 useReducer
为例
import React, { useReducer, useMemo } from 'react'; const [state, dispatch] = useReducer({ dataList: [], }, (state, reducer) => { switch(reducer.type) { case 'update': return { ...state, ...reducer.payload } case default: return state; } })
批量选择在React中我们也可以选择格外增加一个属性来标记是否已被选择,可以在每条数据item被勾选时,比如第一条的话就是:
state.dataList[0].checked = !!state.dataList[0].checked; dispatch({ type: 'update', payload: { dataList: state.dataList.slice() } })
对的,我们需要额外增加 state.dataList.slice()
这一步,这样我们就使用了新的数组引用了。
同理,下面的这些都需要额外增加这一步,或者要实现类似的功能。 选中条数
const count = useMemo(() => { state.dataList.filter(item => item.checked).length }, [state.dataList]);
全选
dispatch({ type: 'update', payload: { dataList: state.dataList.map(item => ({ ...item, checked: true, })) } })
反选
dispatch({ type: 'update', payload: { dataList: state.dataList.map(item => ({ ...item, checked: false, })) } })
React实现二
网上也有不少网友借助一个存储对象来实现上述批量选择、选中条数、全选、反选的功能的,也简单看下具体实现。
import React, { useReducer, useMemo } from 'react'; const [state, dispatch] = useReducer({ dataList: [], checkedMap: {}, // 新增 用来存储选中数据关系 }, (state, reducer) => { switch(reducer.type) { case 'update': return { ...state, ...reducer.payload } case default: return state; } })
批量选择在每条数据item被勾选时通过变更 checkedMap
记录存储关系,比如第一条的话,如果选中就是 {0: true}
,否则就是未选中,具体实现如下:
dispatch({ type: 'update', payload: { checkedMap: { ...state.checkedMap, 0: !!state.checkedMap[0] } } })
我们不需要变更 dataList
里面的数据,只用变更 checkedMap
。
选中条数
const count = useMemo(() => { Object.keys(state.checkedMap).filter(item => state.checkedMap[item]).length }, [state.checkedMap]);
全选
dispatch({ type: 'update', payload: { checkedMap: state.dataList.reduce((acc, curr) => ({ ...acc, [curr.id]: true, }), {}) } })
反选
dispatch({ type: 'update', payload: { checkedMap: state.dataList.reduce((acc, curr) => ({ ...acc, [curr.id]: false, }), {}) } })