Managing direct DOM manipulation in React can be tricky, but sometimes it’s essential for tasks like focusing an input field, scrolling to a specific element, or integrating with third-party libraries. That’s where refs come into play. In this post, we’ll dive deep into how to effectively use refs in React with TypeScript, ensuring type safety and maintainable code. We’ll cover various ref types, use cases, and best practices for incorporating them into your projects.
Understanding React Refs
Refs provide a way to access DOM elements or React component instances created within the render method. They act as a bridge between your React code and the underlying DOM structure, allowing for imperative modifications that complement React’s declarative approach.
While React encourages managing UI updates through state and props, refs are indispensable when direct DOM access is unavoidable. Think of scenarios like controlling focus, text selection, or triggering animations that require precise manipulation beyond React’s reconciliation cycle.
Consider using refs for direct DOM manipulation, measuring DOM elements, or managing focus, text selection, and media playback. Avoid using refs for anything that can be done declaratively with state and props.
Creating and Accessing Refs with TypeScript
TypeScript adds a layer of type safety, ensuring that your refs are correctly typed and preventing runtime errors. Let’s explore how to create and access refs using different approaches.
Using createRef
The createRef function from React is the standard way to create refs. With TypeScript, you can specify the type of the element or component the ref will point to, enhancing code clarity and maintainability. Here’s how it looks:
import React, { createRef, useRef } from 'react'; interface MyComponentProps { // ... other props } const MyComponent: React.FC<MyComponentProps> = () => { const inputRef = useRef<HTMLInputElement>(null); const handleClick = () => { if (inputRef.current) { inputRef.current.focus(); } }; return ( <div> <input type="text" ref={inputRef} /> <button onClick={handleClick}>Focus Input</button> </div> ); }; export default MyComponent;
Callback Refs
Another approach is using callback refs, which provide more control over when the ref is set. This can be useful in certain situations, but the useRef hook is generally preferred for its simplicity and less verbose syntax.
Common Use Cases for Refs
Refs are versatile tools for handling specific interactions and integrations. Let’s examine some practical scenarios.
Managing Focus, Text Selection, and Media Playback
Refs are invaluable for controlling input focus, selecting text within a textarea, or manipulating media playback. These actions typically require direct DOM interaction.
Integrating with Third-Party Libraries
When working with third-party libraries that require direct DOM access, refs act as a bridge to connect your React components with those libraries.
Triggering Imperative Animations
While React provides declarative animation libraries, refs can be helpful for integrating with imperative animation APIs or handling specific animation requirements.
Best Practices and Considerations
When using refs, it’s crucial to follow best practices to ensure code quality and avoid potential issues.
- Use refs sparingly. Prioritize React’s declarative approach whenever possible.
- Avoid accessing the ref’s current value in the render method. This can lead to unexpected behavior.
Understanding these best practices will lead to more maintainable and predictable code.
FAQ
Q: What is the difference between createRef and useRef?
A: While both create refs, useRef is preferred within functional components because it persists the ref value between renders. createRef creates a new ref instance with each render.
By understanding how to use refs effectively with TypeScript in React, you can unlock greater control over your components while maintaining type safety and code clarity. Remember to use refs judiciously, prioritize React’s declarative approach, and follow best practices for predictable and maintainable code. Check out these resources for more information: React’s official documentation on Refs, TypeScript Documentation, and w3Schools React Refs Tutorial. Also, explore more advanced React concepts here.
- Identify when direct DOM manipulation is necessary.
- Choose the appropriate ref type:
useRefor callback ref. - Access the ref’s current value to interact with the DOM element.
- Refs provide a bridge between React code and the DOM.
- TypeScript enhances type safety when using refs.
[Infographic depicting different ref types and their usage]
Question & Answer :
I’m using Typescript with React. I’m having trouble understanding how to use refs so as to get static typing and intellisense with respect to the react nodes referenced by the refs. My code is as follows.
import * as React from 'react'; interface AppState { count: number; } interface AppProps { steps: number; } interface AppRefs { stepInput: HTMLInputElement; } export default class TestApp extends React.Component<AppProps, AppState> { constructor(props: AppProps) { super(props); this.state = { count: 0 }; } incrementCounter() { this.setState({count: this.state.count + 1}); } render() { return ( <div> <h1>Hello World</h1> <input type="text" ref="stepInput" /> <button onClick={() => this.incrementCounter()}>Increment</button> Count : {this.state.count} </div> ); }}
If you’re using React 16.3+, the suggested way to create refs is using React.createRef().
class TestApp extends React.Component<AppProps, AppState> { private stepInput: React.RefObject<HTMLInputElement>; constructor(props) { super(props); this.stepInput = React.createRef(); } render() { return <input type="text" ref={this.stepInput} />; } }
When the component mounts, the ref attribute’s current property will be assigned to the referenced component/DOM element and assigned back to null when it unmounts. So, for example, you can access it using this.stepInput.current.
For more on RefObject, see @apieceofbart’s answer or the PR createRef() was added in.
If you’re using an earlier version of React (<16.3) or need more fine-grained control over when refs are set and unset, you can use “callback refs”.
class TestApp extends React.Component<AppProps, AppState> { private stepInput: HTMLInputElement; constructor(props) { super(props); this.stepInput = null; this.setStepInputRef = element => { this.stepInput = element; }; } render() { return <input type="text" ref={this.setStepInputRef} /> } }
When the component mounts, React will call the ref callback with the DOM element, and will call it with null when it unmounts. So, for example, you can access it simply using this.stepInput.
By defining the ref callback as a bound method on the class as opposed to an inline function (as in a previous version of this answer), you can avoid the callback getting called twice during updates.
There used to be an API where the ref attribute was a string (see Akshar Patel’s answer), but due to some issues, string refs are strongly discouraged and will eventually be removed.
Edited May 22, 2018 to add the new way of doing refs in React 16.3. Thanks @apieceofbart for pointing out that there was a new way.