pidan

pidan

写代码是因为爱,写到世界充满爱!
medium
twitter
telegram
follow

Usage scenarios of ref, forwardRef, and useImperativeHandle in React

Introduction#

In React, ref and forwardRef are not always necessary. React encourages building UI in a declarative way, driving view updates through props and state. ref and forwardRef are more imperative operations, typically used when direct DOM manipulation or access to component instances is required.

Use Cases#

When is it necessary (or highly recommended) to use ref and forwardRef:

1. Directly manipulating DOM elements:

This is the primary and most classic application of ref. In the declarative world of React, we usually avoid direct DOM manipulation. However, in some scenarios, it is necessary or more convenient and efficient to manipulate the DOM directly:

  • Focus Management:
    • Focusing on a specific element: For example, automatically focusing on the first input box after a form loads, or focusing on the first interactive element after a modal opens.
    • Performing actions on blur: For instance, triggering validation when an input box loses focus.
  • Text Selection and Manipulation:
    • Programmatically selecting text in an input box: For example, automatically selecting all text in an input box after clicking a button.
    • Getting or setting the cursor position in a text box.
  • Media Element Control:
    • Controlling playback, pause, volume, etc., of <video> or <audio> elements: For example, implementing custom video player controls.
    • Listening to events on media elements: For example, listening for the end of video playback.
  • Canvas Element Manipulation:
    • Getting the Context (2D or WebGL) of a Canvas element for drawing operations.
  • Integrating third-party DOM manipulation libraries:
    • When you need to integrate some third-party libraries that manipulate the DOM directly (though this is becoming less common in modern React applications), such as certain older jQuery plugins or chart libraries that require direct access to DOM elements.
  • Triggering native methods on DOM elements:
    • For example, triggering the focus(), blur(), click(), select() methods on an input element.

Example: Focusing on an Input Box

import React, { useRef, useEffect } from 'react';

function MyForm() {
  const inputRef = useRef(null);

  useEffect(() => {
    // Focus on the input element after the component mounts
    inputRef.current.focus();
  }, []);

  return (
    <div>
      <input type="text" ref={inputRef} placeholder="Please enter..." />
      {/* ... other form elements ... */}
    </div>
  );
}

2. Accessing the DOM node of a child component (requires forwardRef):

By default, a parent component cannot directly access the DOM node of a child component through ref. If you want to get the DOM element of a child component in the parent (for example, if the child component is a custom Input component and the parent needs to access its internal <input> element), you need to use forwardRef to pass the ref to the DOM element inside the child component.

Why is forwardRef needed?

This is because ref can only be bound to a component instance (for class components) or a DOM element by default. For functional components, ref will bind to the component itself by default, but functional components do not have instances, so ref is usually null. forwardRef allows you to forward the ref passed from the parent component to a specific DOM element or class component instance in the child component.

Example: Parent Component Accessing Child Component's Input DOM Element

import React, { useRef, forwardRef, useImperativeHandle, useEffect } from 'react';

// Child component - Custom Input component
const CustomInput = forwardRef((props, ref) => {
  const inputElementRef = useRef(null);

  // Use useImperativeHandle to expose methods of the child component (optional, not mandatory for forwardRef)
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputElementRef.current.focus();
    },
    getValue: () => {
      return inputElementRef.current.value;
    }
  }));

  return (
    <div>
      <label htmlFor={props.id}>{props.label}: </label>
      <input type="text" id={props.id} ref={inputElementRef} {...props} />
    </div>
  );
});
CustomInput.displayName = 'CustomInput'; // For easier display in React DevTools

// Parent component
function ParentComponent() {
  const customInputRef = useRef(null);

  useEffect(() => {
    // Focus on the input element inside the CustomInput component after the component mounts
    customInputRef.current.focus();

    // Get the value of the input inside the CustomInput component (through the method exposed by useImperativeHandle)
    // const inputValue = customInputRef.current.getValue();
    // console.log("Input Value:", inputValue);

  }, []);

  return (
    <div>
      <CustomInput label="Name" id="nameInput" ref={customInputRef} />
      {/* ... other content ... */}
    </div>
  );
}

3. In certain specific component libraries or scenarios where access to component instances is needed (using useImperativeHandle in conjunction with forwardRef):

Although React promotes a data-driven approach, there are situations where you may need to imperatively call methods on child components in the parent component, such as:

  • Controlling the internal state or behavior of child components: For example, triggering an open() or close() method on a modal component, or calling a refreshData() method on a table component.
  • Integrating certain third-party UI component libraries: Some component libraries may provide imperative APIs that require obtaining the component instance through ref to call their methods.

Using useImperativeHandle to control the API exposed to the parent component:

When you need to allow the parent component to access methods of the child component through ref, you typically combine forwardRef with the useImperativeHandle hook. useImperativeHandle allows you to precisely control which methods and properties can be exposed to the parent component through ref, avoiding exposing all internal details.

When to avoid using ref and forwardRef:

  • Everything should prioritize a declarative approach (props and state). Try to control the behavior and state of child components through props and callback functions.
  • Avoid excessive use of ref for communication between components. Overusing ref can complicate component relationships and make them harder to maintain, disrupting React's data flow.
  • Do not use ref to access the state of child components. Data should be passed through props, allowing child components to pass data back to the parent component through callback functions (lifting state up).
  • Do not misuse forwardRef without a clear necessity. If the parent component does not need direct access to the child component's DOM or instance, there is no need to use forwardRef.

Conclusion:

ref and forwardRef are powerful tools in React, but they should be used with caution. They are necessary or very useful in the following situations:

  • When direct manipulation of DOM elements is needed (e.g., focus management, media control, etc.).
  • When access to the DOM node of a child component is required (using forwardRef).
  • In certain specific scenarios, when imperative calls to child component methods are needed (using forwardRef and useImperativeHandle).

Remember to prioritize declarative approaches to solve problems. Only consider using ref and forwardRef when declarative methods are difficult to implement or when imperative operations are more efficient and concise. Avoid overusing them to maintain code clarity and maintainability.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.