useCascader
级联选择器 Hook
#代码演示
#单选
默认为单选,可以通过 checkNode
方法选中任意节点,是否只选择叶子节点由用户自行控制
Live Editor
Copy
function Example() {const [loading, setLoading] = useState(true)const [treeData, setTreeData] = useState([])useEffect(() => {setTimeout(() => {setTreeData(cityTreeData)setLoading(false)}, 2000)}, [])const {value,expandList,selectNode,checkNode,} = useCascader(treeData, {depth: 3,valueKey: 'id',valueType: 'leaf',emitPath: false,})return (<div><Spin spinning={loading}><div style={{ display: 'flex', minHeight: 300 }}>{(expandList || []).map((nodes, index) => {return (<divkey={index}style={{padding: 10,width: 170,marginRight: 20,border: '1px solid #f0f0f0',}}><Radio.Groupstyle={{ width: '100%' }}value={value && value.id}onChange={(e) => checkNode(e.target.value)}><Liststyle={{height: 300,overflow: 'auto',}}itemLayout="horizontal"dataSource={nodes}renderItem={item => {const { node, checked, selected } = itemconst isLeaf = index === 2return (<List.Itemstyle={{cursor: 'pointer',color: selected ? '#409eff' : 'inherit',}}><div><Radio value={node.id} /><spanstyle={{ marginLeft: 10 }}onClick={() => {if (!isLeaf) {selectNode(node)}}}>{ node.name }</span></div></List.Item>)}}/></Radio.Group></div>)})}</div></Spin><div style={{ marginTop: 20 }}><ReactJsonViewname="value"src={value}collapsed={true}displayDataTypes={false}/></div></div>)}
#多选(按照父子节点互相不关联)
设置 checkStrictly = true
,表示严格遵守父子节点互相不关联
Live Editor
Copy
function Example() {const [loading, setLoading] = useState(true)const [treeData, setTreeData] = useState([])useEffect(() => {setTimeout(() => {setTreeData(cityTreeData)setLoading(false)}, 2000)}, [])const {value,expandList,selectNode,checkNode,} = useCascader(treeData, {depth: 3,valueKey: 'id',emitPath: false,multiple: true,checkStrictly: true,})return (<div><Spin spinning={loading}><div style={{ display: 'flex', minHeight: 300 }}>{(expandList || []).map((nodes, index) => {return (<divkey={index}style={{padding: 10,width: 170,marginRight: 20,border: '1px solid #f0f0f0',}}><Liststyle={{height: 300,overflow: 'auto',}}itemLayout="horizontal"dataSource={nodes}renderItem={item => {const { node, selected, checked, indeterminate } = itemreturn (<List.Itemstyle={{cursor: 'pointer',color: selected ? '#409eff' : 'inherit',}}><div><Checkboxvalue={node.id}checked={checked}indeterminate={indeterminate}onChange={() => {checkNode(node)}}/><spanstyle={{ marginLeft: 10 }}onClick={() => selectNode(node)}>{ node.name }</span></div></List.Item>)}}/></div>)})}</div></Spin><div style={{ marginTop: 20 }}><ReactJsonViewname="value"src={value}collapsed={true}displayDataTypes={false}/></div></div>)}
#多选(记录叶子节点)
Live Editor
Copy
function Example() {const [loading, setLoading] = useState(true)const [treeData, setTreeData] = useState([])useEffect(() => {setTimeout(() => {setTreeData(cityTreeData)setLoading(false)}, 2000)}, [])const {value,expandList,selectNode,checkNode,} = useCascader(treeData, {depth: 3,valueKey: 'id',valueType: 'leaf',emitPath: true,multiple: true,})return (<div><Spin spinning={loading}><div style={{ display: 'flex', minHeight: 300 }}>{(expandList || []).map((nodes, index) => {return (<divkey={index}style={{padding: 10,width: 170,marginRight: 20,border: '1px solid #f0f0f0',}}><Liststyle={{height: 300,overflow: 'auto',}}itemLayout="horizontal"dataSource={nodes}renderItem={item => {const { node, selected, checked, indeterminate } = itemreturn (<List.Itemstyle={{cursor: 'pointer',color: selected ? '#409eff' : 'inherit',}}><div><Checkboxvalue={node.id}checked={checked}indeterminate={indeterminate}onChange={() => {checkNode(node)}}/><spanstyle={{ marginLeft: 10 }}onClick={() => selectNode(node)}>{ node.name }</span></div></List.Item>)}}/></div>)})}</div></Spin><div style={{ marginTop: 20 }}><ReactJsonViewname="value"src={value}collapsed={true}displayDataTypes={false}/></div></div>)}
#多选(记录典型结点)
设置 valueType = branch
后,记录到 value
中的为典型结点(当某个层级的结点全选时,对应的父结点即为典型结点)
Live Editor
Copy
function Example() {const [loading, setLoading] = useState(true)const [treeData, setTreeData] = useState([])useEffect(() => {setTimeout(() => {setTreeData(cityTreeData)setLoading(false)}, 2000)}, [])const {value,expandList,selectNode,checkNode,} = useCascader(treeData, {depth: 3,valueKey: 'id',valueType: 'branch',emitPath: false,multiple: true,})return (<div><Spin spinning={loading}><div style={{ display: 'flex', minHeight: 300 }}>{(expandList || []).map((nodes, index) => {return (<divkey={index}style={{padding: 10,width: 170,marginRight: 20,border: '1px solid #f0f0f0',}}><Liststyle={{height: 300,overflow: 'auto',}}itemLayout="horizontal"dataSource={nodes}renderItem={item => {const { node, selected, checked, indeterminate } = itemreturn (<List.Itemstyle={{cursor: 'pointer',color: selected ? '#409eff' : 'inherit',}}><div><Checkboxvalue={node.id}checked={checked}indeterminate={indeterminate}onChange={() => {checkNode(node)}}/><spanstyle={{ marginLeft: 10 }}onClick={() => selectNode(node)}>{ node.name }</span></div></List.Item>)}}/></div>)})}</div></Spin><div style={{ marginTop: 20 }}><ReactJsonViewname="value"src={value}collapsed={true}displayDataTypes={false}/></div></div>)}
#异步加载结点
配置 lazyLoad
, 可实现结点异步加载,loadFresh
控制是否每次都重新加载结点
Live Editor
Copy
function Example() {const {value,expandList,selectNode,checkNode,} = useCascader(null, {depth: 3,valueKey: 'id',valueType: 'branch',emitPath: false,multiple: true,lazyLoad: (node, level, { resolve, reject }) => {setTimeout(() => {if (!node) { // 第一层级resolve(cityTreeData.map((item) => {return {...item,children: undefined,}}))} else {// DFSlet matchNode = nullconst stack = [{node: { children: cityTreeData },level: -1,}]while (stack.length !== 0 && !matchNode) {const { node: _node, level: _level } = stack.pop()const children = _node.children || []for (let i = children.length - 1; i >= 0; i--) {if (_level + 1 === level) {if (children[i].id === node.id) {matchNode = {...children[i],children: (children[i].children || []).map(child => {return {...child,children: undefined,}}),}break}} else {stack.push({node: children[i],level: _level + 1,})}}}if (matchNode) {resolve(matchNode.children || [])} else {resolve([])}}}, 1000)}})return (<div><div style={{ display: 'flex', minHeight: 300 }}>{(expandList || []).map((nodes, index) => {return (<divkey={index}style={{padding: 10,width: 170,marginRight: 20,border: '1px solid #f0f0f0',}}><Liststyle={{height: 300,overflow: 'auto',}}itemLayout="horizontal"dataSource={nodes}renderItem={item => {const { node, selected, checked, indeterminate, loading } = itemreturn (<List.Itemstyle={{cursor: 'pointer',color: selected ? '#409eff' : 'inherit',}}><div><Checkboxvalue={node.id}checked={checked}indeterminate={indeterminate}onChange={() => {checkNode(node)}}/><spanstyle={{ marginLeft: 10 }}onClick={() => selectNode(node)}>{ node.name }</span>{loading && <LoadingOutlined style={{ marginLeft: 10 }} />}</div></List.Item>)}}/></div>)})}</div><div style={{ marginTop: 20 }}><ReactJsonViewname="value"src={value}collapsed={true}displayDataTypes={false}/></div></div>)}
#API
interface TreeNode {[key: string]: anychildren?: TreeNode[]}type UseCascader = (treeData: TreeNode[], options: Options) => Result
#Options
参数 | 说明 | 必选 | 类型 | 默认值 |
---|---|---|---|---|
valueType | 以哪类节点作为值:leaf :底层叶子节点 branch :子节点全选的节点 | - | 'branch'' | 'leaf' | 'leaf' |
valueKey | 以节点对象的指定 key 作为值的唯一标识 | - | string | 'value' |
depth | 树形结构的深度 | 是 | number | - |
checkStrictly | 是否严格的遵守父子节点不互相关联 | - | boolean | false |
emitPath | 记录的数据( value )是否记录全路径 | - | boolean | true |
multiple | 是否多选 | - | boolean | false |
lazyLoad | 异步加载子节点方法 | - | (node: TreeNode, level: number, {resolve, reject}: {resolve: (children: TreeNode[]) => void; reject: (children: TreeNode[]) => void}) => void | - |
loadFresh | 加载之前异步加载过的子节点时,是否不复用缓存,重新加载新数据 | - | boolean | false |
#Result
参数 | 说明 | 类型 | 备注 |
---|---|---|---|
value | 勾选的值 | TreeNode | TreeNode[] | TreeNode[][] | multiple === false && emitPath === false 时,value => TreeNode ; multiple === false && emitPath === true 时,value => TreeNode[] ; multiple === true && emitPath === false 时,value => TreeNode[] ; multiple === true && emitPath === true 时,value => TreeNode[][] |
expandList | 各层级展开的节点列表 | { node: TreeNode; selected: boolean; checked: boolean; indeterminate: boolean }[][] | - |
checkNode | (取消)勾选节点 | (node: TreeNode | string | number, levelOrChecked?: number | boolean, checked?: boolean)=> void | 默认为 toggle 模式,可通过显式传入 checked 明确操作;传入完整的节点数据和对应的 level ,可以提交执行效率 |
selectNode | (取消)选择某个节点以展开/折叠子节点 | (node: TreeNode | string | number, level?: number) => void | 传入完整的节点数据和对应的 level ,可以提交执行效率 |