实现全选
主要是通过选中当前元素时,触发一个外层的onChange,把当前元素和子元素全部传出去,然后让用户监听onChange拿到值来修改,所以我们需要再onChange的时候对它的子元素进行递归遍历然后拍平放到一个一层的数组里
- tree.exmaple.tsx
const NewTreeExample: React.FC = () => {
const [selectedValues, setSelectedValues] = useState<string[]>([])
// const [selectedValue, setSelectValue] = useState('1.1')
// const [selectValue, setSelectValue] = useState('1.1')
return (
<NewTree sourceData={sourceData} onChange={(value: string[]) => setSelectedValues(value)}
selected={selectedValues} multiple={true}
/>
)
}
- tree.tsx
interface RecursiveArray<T> extends Array<T | RecursiveArray<T>>{
}
const collectChildrenValues = (item: SourceDataItem): any => {
return flatten(item.children?.map(i => [i.value, collectChildrenValues(i)]))
}
const flatten = (arr?: RecursiveArray<string>): string[] => {
if (!arr) return []
return arr.reduce<string[]>((result, current) =>
result.concat(typeof current === 'string' ? current : flatten(current)), [])
}
const onChange: ChangeEventHandler<HTMLInputElement> = (e) => {
const childrenValues = collectChildrenValues(data)
console.log(childrenValues, 'aaa')
if (treeProps.multiple) {
if (e.target.checked) {
treeProps.onChange([...treeProps.selected, data.value, ...childrenValues])
} else {
treeProps.onChange(treeProps.selected.filter(value => value !== data.value && childrenValues.indexOf(value) === -1))
}
}
半选实现思路
给递归的组件传一个单独的onItemChange,每次点击当前元素的时候拿到(当前所有选中的元素和它下面的子元素)和(当前元素的所有子元素进行比较),找出相同的元素,然后判断相同的元素的个数如果不等于0的话,就再继续调父元素的onItemChange把包含当前元素和当前元素下的子元素的数组里添加当前数据的父value,然后在最外层的newItem中拿到值调用使用组件时接收的onChange把值传出去,这时候我们的全选和半选只需要在tree-item组件里判断就可以了,这里我们使用indeterminate来设置checkbox的状态,这里要注意每次传值的时候我们都要去重一下
- tree-item.tsx
// 根据包含当前元素的子元素和不包含当前元素的子元素查找相同元素
function intersect<T>(array1: T[], array2: T[]): T[] {
const result: T[] = []
for (let i = 0; i < array1.length; i++) {
if (array2.indexOf(array1[i]) >= 0) {
result.push(array1[i])
}
}
return result
}
const onItemChange = (values: string[]) => {
// values是当前选中的所有元素,childernValues是当前元素下的所有元素
//如果他们长度一样说明是全选,不一样说明是半选,长度是0说明是全不选
const childrenValues = collectChildrenValues(data)
const common = intersect(values, childrenValues)
if (common.length !== 0) {
// 每次先去重
props.onItemChange(Array.from(new Set(values.concat(data.value))))
if (common.length === childrenValues.length) {
// 全选
inputRef.current!.indeterminate = false
} else {
// 半选
inputRef.current!.indeterminate = true
}
} else {
// 全不选
inputRef.current!.indeterminate = false
}
}
const inputRef = useRef<HTMLInputElement>(null)
- tree.tsx
const NewTree: React.FC<TreeProps> = (props) => {
const onItemChange = (values: string[] | string) => {
if (props.multiple) {
// 先去重
props.onChange(Array.from(new Set(values)) as string[])
} else {
props.onChange(values as string)
}
}
return (
<div>
{props.sourceData?.map(item =>
<TreeItem treeProps={props} data={item} level={1} key={item.value} onItemChange={onItemChange}
/>
)}
</div>
)
}
完整代码:https://github.com/wanglifa/react-tree-ts/blob/master/src/tree/tree.tsx
网友评论