帮同事debug发现的问题,记录一下,
弗曼学习大法好
interface RefFunType {
hdlUpdate(): void,
name: string
}
父组件使用useRef
useRef()
传入的泛型是子组件存放在ref.current中的数据格式,
(如果子组件内部绑定了自己的ref,那用的泛型为useRef
ref是绑定在自定义组件上,子组件直接从props中获取ref是获取不到的,
这是因为ref不是prop属性,会像key一样,被React进行特殊处理
所以需要使用
React.forwardRef((props: any, React.Ref | undefined) => {...})
将ref再重新转发到子组件中
在子组件中使用useImperativeHandle
useImperativeHandle(ref, () => ({
[key:string]: any
}));
第一个param为转发的ref,第二个参数为一个fun,返回一个对象,这个对象就是可以在父组件中通过ref.current?.XXX获取到的方法和数据
在父组件中,需要注意的是,ref 对象内容发生变化时,useRef 并不会通知你,当.current改变时,组件并不会rerender。
因此,当我们需要在父组件中使用子组件的数据时,有两种方法。
useEffect(() => {
setName(childRef.current!.name)
})
const sexRef = useCallback(node => {
console.log('node && node.name', node && node.name)
node && setSex(node.name);
}, []);
而需要在父组件中调用子组件的方法的时候,我们可以使用这种方法
将子组件的方法嵌套多一层方法,如果不创建一个自己的方法的话,在第一次render的时候,current中的方法还没有创建完成,所以我们会将undefined传递给onClick。
而当我们创建了一个自己的方法,在内部去调用current.hdlUpdate的时候,此时调用的是current这个引用上的方法(把ref看成一个不会通知组件render,也不会重新创建的对象),此时hdlUpdate方法已经存在了。
一个比较具体的例子:
interface RefFunType {
hdlUpdate(): void,
name: string
}
// 子组件,使用forwardRef转发
const NameChild = forwardRef((props: any, ref: React.Ref | undefined) => {
const [name, setName] = useState('jack');
useImperativeHandle(
ref,
// 将需要的方法暴露出去
() => ({
hdlUpdate: () => {
console.log('name', name)
},
name: 'name'
})
)
return <>
name: setName(e.target.value)}>
>
});
// 另一个一样的子组件
const SexChild = forwardRef((props: any, ref: React.Ref | undefined) => {
const [sex, setSex] = useState('man');
useImperativeHandle(
ref,
() => ({
hdlUpdate: () => {
console.log('sex', sex)
},
name: sex
})
)
return <>
sex: setSex(e.target.value)}>
>
})
interface SwitchComponentProps {
Component: React.ReactNode
}
// 中间组件,用来测试每次调用的current是否是当前子组件的,和直接在父组件写if渲染一样
const SwitchComponent:FC = ({Component}) => {
return
{Component}
}
// 父组件
const Index: FC = (props) => {
const [type, setType] = useState(0);
const [name, setName] = useState("");
// 创建ref
const childRef = useRef();
const childrens = [
{ component: },
{ component: }
];
// 获取数据
useEffect(() => {
setName(childRef.current!.name)
})
// console.log('childRef.current', childRef.current)
// const timer = setInterval(() => {
// console.log('childRef.current', childRef.current)
// }, 3000)
// 在onClick绑定自己的方法,调用方法
return
{name}
}
这个是官方父组件调用子组件中的input的focus方法的例子
const FancyInput = forwardRef((props: any, ref: React.Ref | undefined) => {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current?.focus();
}
}));
return ;
})