Ref in React
some little tech note about React and AntD
Let's talk a little about ref
- basic usage example, we refer to the Dom. we prefer the sample1
// in ts
const refSample1 = React.useRef<HTMLDivElement>();
const refSample2 = React.useRef<HTMLDivElement|null>();
render(){
return (
<>
<div ref={refSample1}>
Hello I am div element
</div>
<div ref = {node=>{refSample2.current = node;}}>
Hello I am div element2
</div>
</>
)
}
- customized Ref, ref is not only a dom, see this example
//ts
interface SampleRefProps{
onClick:()=>void,
wrapperElement:HTMLDivElement,
}
// in component1
const SampleComponent:React.RefForwardingComponent<SampleRefProps,any>=
(props,ref)=>{
const divRef = React.uesRef<HTMLDivElement>();
React.useImperativeHandle(ref,()=>({
onClick:()=>{console.log('hello click')},
wrapperElement:divRef.current,
}));
return (
<>
<div ref = {divRef}>Hello I am body</div>
</>
)
}
// in Component2
const Demo:React.FC = ()=>{
const ref = React.useRef<SampleRefProps>();
return (
<SampleComponent ref = {ref}/>
)
}
- When ref.current will be updated?
according to the offical documents refs updates Before ComponentDidMount or ComponentDidUpdate or ComponentDidUnmount, that is straightforward for Class component, but for function component, I believe it will be updated after function execution but, before React.Effects() or React.LayoutEffects().- I just give a summary here, for function component, the order is render > React.useImperativeHandle > useLayoutEffect > useEffect (This is parallel with Browser repaint). Bellow is a code snappit
interface RefProps{
element:HTMLDivElement|HTMLSpanElement|null;
};
const RefFC=React.forwardRef<RefProps,{isdiv:boolean}>(({isdiv=true},ref)=>{
const domRef = React.useRef<HTMLSpanElement|HTMLDivElement>(null);
// here just for demo, we can attach ref to dom directlly
React.useImperativeHandle(ref,()=>{
console.log(`useImperativeHandle executed`)
return {
element:domRef.current,
};
})
React.useEffect(()=>{
console.log('effect of RefFC execution');
})
console.log(`befrore redner execution of RefFC`);
return (
<>
{
isdiv&&<div ref={domRef as any}>Hello I am div</div>
}
{
!isdiv&&<span ref={domRef as any}>Hello I am span</span>
}
</>
);
})
const RefDemo:React.FC=()=>{
const refSample1 = React.useRef<HTMLDivElement>(null);
const refSample2 = React.useRef<RefProps>(null);
const [isdiv,setIsdiv]=React.useState<boolean>(true);
React.useEffect(()=>{
console.log(`This is Effect,refSample1 ${refSample1.current}`);
console.log(`This is Effect,refSample2 ${refSample2.current?.element}`);
});
React.useLayoutEffect(()=>{
console.log(`This is LayoutEffect:refSample1 ${refSample1.current}`);
console.log(`This is LayoutEffect: refSample2: ${refSample2.current?.element}`)
})
console.log(`Before RefDemo Render return, refsample1 ${refSample1.current}`);
console.log(`Before RefDemo Render return, refsample2 ${refSample2.current?.element}`);
return (
<>
<div className="each-example">
<h2>Check console, it will illustrate when ref will be populated and updated</h2>
<p>For Functional Component, it will be updated after render and before Effect and Layout Effect</p>
<div ref={refSample1}>
Hello I am Body refSample1
</div>
<div>
<Button onClick={()=>setIsdiv(pre=>!pre)}>Click to Switch between div and span</Button>
</div>
<p>Bellow is refSample2</p>
<RefFC ref ={refSample2} isdiv={isdiv}/>
<p>
In Conslusion, for function component ref, is populated or updated after function return, but before Effect and layoutEffect.
</p>
</div>
</>
);
}
Let's talk about the React.forwardRef<RefProps,Props>() and React.ForwardRefRenderFunction<RefProps,Props> type
lange is cheap, let's see the code. See, React.ForwardRefRenderFunction is the parameter type of forwardRef funciton.
function forwardRef<T, P = {}>(render: ForwardRefRenderFunction<T, P>): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>;