9.1 笔记
知识点整理
forwardRef的使用
使用场景
INFO
当你需要将子组件的DOM节点暴露给父组件使用的时候
代码
import { useRef, useEffect, forwardRef } from "react";
export function Parent() {
const elementRef = useRef();
useEffect(() => {
// Works!
console.log(elementRef.current); // logs HTMLDivElement <div>Hello, World!</div>
}, []);
return <Child ref={elementRef} />; // assign the ref
}
const Child = forwardRef(function (props, ref) {
return <div ref={ref}>Hello, World!</div>;
});
forwardRef(props,ref)
props
:就是父组件传给子组件的参数(不包括ref)ref
:就是父组件传递给子组件的ref
(这个ref
会放置到父组件想要访问子组件的DOM的元素身上)
useImperativeHandle使用
使用场景
INFO
一般这个Hooks
都是和useforwardRef
一起使用的,作用就是,当你使用forwardRef
的时候暴露的其实是子组件的整个dom。这个方法就是,你只暴露其中的某几个方法,让父组件使用。
代码
import { useRef, useEffect, forwardRef, useImperativeHandle } from "react";
export function Parent() {
const elementRef = useRef();
useEffect(() => {
// Works!
console.log(elementRef.current); // logs undefined
}, []);
return <Child ref={elementRef} params={11} />; // assign the ref
}
const Child = forwardRef(function (props, ref) {
// 使用useImperativeHandle暴露其中一部分
useImperativeHandle(ref,()=>{
})
return <div ref={ref}>Hello, World!</div>;
});
就像上面那样,我们其实并没有暴露,所以父组件里面接收到的的就是undefined
import { useRef, forwardRef, useImperativeHandle } from "react";
export function Main() {
const methodsRef = useRef();
const focus = () => methodsRef.current.focus();
const blur = () => methodsRef.current.blur();
return (
<>
<FocusableInput ref={methodsRef} />
<button onClick={focus}>Focus input</button>
<button onClick={blur}>Blur input</button>
</>
);
}
const FocusableInput = forwardRef(function (props, ref) {
const inputRef = useRef();
useImperativeHandle(
ref,
function () {
// 暴露出ref身上的focus和blur方法
return {
focus() {
inputRef.current.focus();
},
blur() {
inputRef.current.blur();
}
};
},
[]
);
return <input type="text" ref={inputRef} />;
});
代码解读:
- 首先父组件创建一个
ref
并将它传递给子组件 - 子组件使用
forwardRef
包裹,这样的父组件就可以使用子组件的ref
了 - 子组件将
ref
用useImperativeHandle()
包裹起来,这样就可以限制父组件可以访问的DOM
元素的范围 - 然后父组件直接去访问就可以了
useImperativehandle(ref,createhandle,dependencies)
ref
:这个就是父组件传给子组件的那个ref
,就是forwardRef(props,ref)
的第二个参数createhandle
:这是一个函数,返回的返回值是一个对象,对象里面是你所有想要暴露出去的函数dependencies
:createhandle
代码中所用到的反应式的值的列表
INFO
但是需要注意的是,不要滥用ref,如果这个操作可以通过props来实现,那么就少用ref
参考资料:
useImperativeHandle – React 中文文档
React forwardRef(): How to Pass Refs to Child Components
项目整理
场景 select双栏展示
现在对于表单的item
,希望他是一个select
搜索框,并且它的下拉列表需要多列展示,就像下图展示的这样。
思路
主要思路就是,我们希望下拉框里面展示的是一个二维的table
,那么我们只要借助antd select
的dropdownRender
这个API
就可以实现了。
INFO
dropdownRender
的作用:就是定义下拉框的内容。我们将table
组件放在这个方法里面,让他渲染出来即可。
假如你需要实现,选中一行,对选中的行进行某些操作的时候,这时候就不能使用**onSelect**
了,因为你点击的其实是表格里面的一行,所以你需要借助**onRow**
这个方法
INFO
onRow
的作用,用来设置行属性,它里面可以监听一些事件,包括点击,双击,鼠标悬停,鼠标移入等
<Table
onRow={(record) => {
return {
onClick: (event) => {}, // 点击行
onDoubleClick: (event) => {},
onContextMenu: (event) => {},
onMouseEnter: (event) => {}, // 鼠标移入行
onMouseLeave: (event) => {},
};
}}
onHeaderRow={(columns, index) => {
return {
onClick: () => {}, // 点击表头行
};
}}
/>
为了提高用户的体验,一般情况下,当用户选中一行以后,就建议将这个下拉框收回去(但是我还没有实现)
代码
<ProFormSelect
width="md"
name="bomCode"
label="机种料号"
showSearch
placeholder="请选择"
// request发送请求获取
request={async ({ keyWords }) => {
let res = await getBomData({ pageSize: 10, current: 1, bomCode: keyWords })
if (res.success) {
setBomTable(res.data)
}
return []
}}
// 校验规则
rules={[
{
required: true,
message: '请选择机种料号!',
},
]}
// 设置防抖时间
debounceTime={500}
// 自定义下拉框内容
fieldProps={{
// 设置下拉框的内容,是一个表格
dropdownRender: () => {
return (
<div style={{ height: '200px', overflowY: 'auto' }}>
<Table
size="small"
showHeader={false}
columns={bomColumns}
dataSource={bomTable}
pagination={false}
rowKey={(record: { bomCode: string, bomName: string, bomSpec: string }) => record.bomCode}
// 设置点击行的时候,将数据回填到表单中
onRow={(record: { bomCode: string, bomName: string, bomSpec: string }) => {
return {
onClick: () => {
formRef.current?.setFieldsValue({
bomCode: record.bomCode,
bomName: record.bomName,
bomSpec: record.bomSpec,
})
}
}
}}
/>
</div>
)
},
// 当清空的时候,将表单的值也清空
onClear: () => {
formRef.current?.setFieldsValue({
bomName: "",
bomSpec: "",
})
}
}}
>
</ProFormSelect>