Skip to content

Instantly share code, notes, and snippets.

@Venryx
Last active February 9, 2023 22:36
Show Gist options
  • Star 23 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Venryx/7cff24b17867da305fff12c6f8ef6f96 to your computer and use it in GitHub Desktop.
Save Venryx/7cff24b17867da305fff12c6f8ef6f96 to your computer and use it in GitHub Desktop.
Using "useImperativeHandle" in a React functional component, with automatic TypeScript typing
import {forwardRef, useImperativeHandle, ForwardRefExoticComponent, RefAttributes, Ref} from "react";
export type Handle<T> = T extends ForwardRefExoticComponent<RefAttributes<infer T2>> ? T2 : never;
export const Parent = (props: {})=> {
let childHandle: Handle<typeof Child>;
return (
<div onClick={()=>childHandle.SayHi()}>
<Child name="Bob" ref={c=>childHandle = c}/>
</div>
);
};
export const Child = forwardRef((props: {name: string}, ref: Ref<{SayHi}>)=> {
const {name} = props;
// expose internal functions; what we return in the callback below is what gets sent to the "ref" callback in Parent
useImperativeHandle(ref, () => ({ SayHi }));
function SayHi() { console.log("Saying hello from: " + name); }
return <div>{name}</div>;
});
@qkreltms
Copy link

qkreltms commented Apr 11, 2020

Great! It works perfect, but It would be great if there are some kinds of explanation of this.


I found that how it works it's simply extract Ref type from forwarding component.

@gleydson
Copy link

gleydson commented Jun 5, 2020

Great! It works perfect, but It would be great if there are some kinds of explanation of this.

I found that how it works it's simply extract Ref type from forwarding component.

That's right, this is necessary when you already have a ref inside a component but you need to insert another ref in a parent component that uses that component to retrieve some information from it

@lilywang711
Copy link

I got ts errors:
TS2339: Property 'SayHi' does not exist on type 'never'.
TS2322: Type '{ SayHi: any; } | null' is not assignable to type 'never'.   Type 'null' is not assignable to type 'never'.

@Venryx
Copy link
Author

Venryx commented Jul 30, 2020

@lilywang711 Are you using the latest versions of TypeScript (I'm at 3.9.6)? And are you using any non-standard TypeScript settings? (perhaps more of the "strict" options?)

For reference, this is (the trimmed version of) my tsconfig.json:

{
	"compilerOptions": {
		"module": "ES2015", // leave the imports/exports alone (webpack will convert them to "require")
		"moduleResolution": "node",
		"target": "esnext", // babel targets es2015 (ie. transpiles our code to it), so it's fine for TS to target es-next
		"lib": [
			"es5",
			"es6",
			"ESNext",
			"dom"
		],
		"jsx": "react",
		"experimentalDecorators": true,
		"allowSyntheticDefaultImports": true,
		"esModuleInterop": true,
		"alwaysStrict": true,
	}
}

@lilywang711
Copy link

@Venryx Yes you are right, when I changed 'strict' from true to false, the error disappeared, thanks!

@Rendez
Copy link

Rendez commented May 3, 2022

Isn't React.ElementRef<typeof Child> doing the same as type Handle<T> = T extends ForwardRefExoticComponent<RefAttributes<infer T2>> ? T2 : never;?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment