Put the original link first
The weather in Hangzhou in April and May this year is always confusing, which may also be the reason for the epidemic this year. It seems that there is not much feeling of spring. Although the temperature is slowly rising up, but the feeling is not the spring of all things recovery, flowers everywhere in full bloom feeling; I do not know whether it is the epidemic this year, or the sight is obscured by common things, in short, everything is not quite as imagined. Without further ado, here’s a Martin Hochel article
The following version is used for the article
{
"@types/react": "16.4.7"."@types/react-dom": "16.0.6"."typescript": "3.0.1"."react": "16.4.2"."react-dom": "16.4.2"
}
Copy the code
The source address
I often get questions about how to properly define react refs types in typescript. I couldn’t find anyone who has written about it, so I wrote this article to help people new to React and typescript.
Disclaimer: Please refer to the React Ref official documentation for more details
What is the Refs
Refs provides a way to access DOM nodes or React elements created in the render method. Refs provides a way to access DOM nodes in React
Create the Refs
class MyComponent extends Component {
private myRef = createRef()
render() {
return <div ref={this.myRef} />}}Copy the code
A component defined this way will receive a compilation error
[ts]
Type 'RefObject<{}>' is not assignable to type 'RefObject<HTMLDivElement>'.
Copy the code
Why will appear this error, ts in JSX file has a very good type inference, we’re in a < div > on increased the ref, ts will deduce the HTMLDivElement type used by need to receive, we can use the following method to solve this problem. Let’s first look at the react. createRef definition:
// react.d.ts
function createRef<T> () :RefObject<T>
// react.d.ts
interface RefObject<T> {
// immutable
readonly current: T | null
}
Copy the code
The RefObject
returned is generic; From this we can know how to solve the above validation error:
Use the Refs
We can access the node directly via the current property defined on ref, as follows
const node = this.myRef.current
Copy the code
However, the node may be null, so we will not use the following code to perform operations on this value, and will receive ts error:
class MyComponent extends Component {
private myRef = React.createRef<HTMLDivElement>()
focus() {
const node = this.myRef.current
node.focus()
}
}
[ts] Object is possibly 'null'.
const node: HTMLDivElement | null
Copy the code
The React document defines current:
React will assign the current property with the DOM element when the component mounts, and assign it back to null when it unmounts. ref updates happen before componentDidMount or componentDidUpdate lifecycle hooks. React assigns the DOM node to Current when a component mounts componentDidMount. Current is null when componentDidUpdate.
This is what TS is trying to tell you by compiling errors. You need to take some safety precautions, which are necessary for non-null judgments using if, to make sure your program doesn’t have runtime errors;
This gives us an automatic reminder of the HTMLDivElement DOM API, which is nice
Add a Ref to a custom Class component
If we want to get focus when we mount a custom component, we can access the custom component instance by ref and call the focus method of the component.
import React, { createRef, Component } from 'react'
class AutoFocusTextInput extends Component {
// create ref with explicit generic parameter
// this time instance of MyComponent
private myCmp = createRef<MyComponent>()
componentDidMount() {
// @FIXME
// non null assertion used, extract this logic to method!
this.textInput.current! .focus() } render() {return <MyComponent ref={this.textInput} />}}Copy the code
⚠ ️
- This approach applies only to custom components declared as classes
- We have access to all instance methods
Pass refs to DOM Components
Define a FancyButton component that renders a native button onto the page
Ref Forwarding is an optional feature of the component. It can take a REF from a component and pass it on to its child components.
In the following example, we add Forwarding Refs to the component so that we can access the button node within the component directly through the REF if necessary, just as we would with the Button node
So what’s actually happening here?
- We create and export a Ref type to the caller of our component
- With the forwardRef, we get the ref and pass it to the child component, which is a normal function with two parameters
// react.d.ts
function forwardRef<T.P = {}>(
Component: RefForwardingComponent<T, P>
): ComponentType<P & ClassAttributes<T>>
// T, P here
1:T is the type of DOM2:P is the Props for passing3The definition of the return value is the component definition that combines the props and ref typesCopy the code
This way we can make type-safe calls:
Isn’t that elegant?
Refs and function components
Because function components have no instances, you might not use the ref attribute on function components; However, when you are inside a function component, you can still access a Dom node or class component through a ref
Forwarding Refs
Ref Forwarding is a technique for automatically passing the Ref hook to a component’s descendants
Forwarding refs is used in higher-order components
The official documentation on how higher-order components use Forwarding refs is somewhat complicated; In short, you can just wrap your component with the forwardRef API
return forwardRef((props, ref) = > {
return <LogProps {. props} forwardedRef={ref} />
})
Copy the code
Here is the FancyButton implemented as a class component
Finally, we implement this higher-order component through Ref Forwarding
Higher-order components that use Ref Forwarding require us to specify parameter types
const EnhancedFancyButton = withPropsLogger<
FancyButton,
FancyButtonProps
>(FancyButton)
Copy the code
So we can use it type-safe
The above! Last rule, post my blog, welcome to follow