Q6. Giải thích về forwardRef và useImperativeHandle trong React Native?

·

5 min read

forwardRef trong React được sử dụng để chuyển tiếp một ref qua một component tới một element con. Điều này hữu ích khi bạn cần truy cập trực tiếp đến một DOM node hoặc một instance của một component con từ một component cha, mà không muốn phá vỡ tính đóng gói của component con

Sử dụng forwardRef

Để minh họa forwardReftính đóng gói trong React, hãy xem xét một ví dụ trong đó một component cha cần truy cập trực tiếp đến một phần tử DOM trong component con để thực hiện một hành động cụ thể, chẳng hạn như focus vào một input. Thông thường, component con không để lộ DOM node của nó, giữ cho cấu trúc và hành vi của nó được đóng gói. Tuy nhiên, bằng cách sử dụng forwardRef component cha có thể đạt được mục đích này mà không vi phạm nguyên tắc đóng gói

Giả sử chúng ta có một component cha tên là ParentComponent và một component con tên là TextInput. ParentComponent muốn focus vào TextInput khi một nút được nhấn.

Bước 1: Tạo TextInput component sử dụng forwardRef

import React, { forwardRef } from 'react';
import { TextInput as RNTextInput } from 'react-native';

const TextInput = forwardRef((props, ref) => {
  return <RNTextInput ref={ref} {...props} />;
});

export default TextInput;

Ở đây, TextInput là một component nhận ref từ component cha và chuyển tiếp ref đó đến phần tử RNTextInput của React Native

Bước 2: Tạo ParentComponent để sử dụng TextInput

import React, { useRef } from 'react';
import { View, Button } from 'react-native';
import TextInput from './TextInput'; // Đảm bảo import đúng đường dẫn

const ParentComponent = () => {
  const inputRef = useRef(null);

  const focusInput = () => {
    if (inputRef.current) {
      inputRef.current.focus();
    }
  };

  return (
    <View>
      <TextInput ref={inputRef} placeholder="Enter text here" />
      <Button title="Focus the input" onPress={focusInput} />
    </View>
  );
};

export default ParentComponent;

Giải thích:

  1. Tính đóng gói: TextInput component duy trì tính đóng gói bằng cách không trực tiếp expose bất kỳ chi tiết triển khai nào của nó. Nó chỉ nhận ref từ forwardRef và chuyển tiếp ref đó đến phần tử RNTextInput

  2. Sử dụng forwardRef: TextInput sử dụng forwardRef để nhận và chuyển tiếp ref. Điều này cho phép ParentComponent có thể truy cập và tương tác trực tiếp với DOM node của TextInput

  3. Component cha: ParenComponent tạo một ref bằng useRef và truyền ref đó vào TextInput. Khi nút focus được nhấn, hàm focusInput sẽ được gọi, và inputRef.current.focus() sẽ focus vào input element.

Khi nào nên sử dụng forwardRef?

  • Khi cần truy cập trực tiếp đến DOM node: Khi component cha cần truy cập hoặc thao tác trực tiếp trên DOM node của component con.

  • Giữ nguyên tính đóng gói: Khi bạn muốn giữ tính đóng gói của component con, không để lộ chi tiết triển khai ra ngoài.

  • Sử dụng HOCs: Khi bạn sử dụng Higher Order Components và cần chuyển tiếp ref đến component gốc.

Sử dụng forwardRefuseImperativeHandle

Trong React, việc truyền callback từ component cha vào component con là một cách phổ biến để gọi một method từ component cha. Tuy nhiên để đảm bảo tính đóng gói và tránh việc truyền callback qua props, bạn có thể sử dụng forwardRef kết hợp với useImperativeHandle. Điều này cho phép bạn expose một số method cụ thể của component con để component cha có thể gọi trực tiếp thông qua ref

Bước 1: Tạo component con sử dụng forwardRefuseImperativeHandle

import React, { useImperativeHandle, forwardRef, useRef } from 'react';
import { TextInput as RNTextInput, Button, View, Text } from 'react-native';

const TextInput = forwardRef((props, ref) => {
  const inputRef = useRef(null);

  // Expose methods to parent component
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
    clear: () => {
      inputRef.current.clear();
    },
  }));

  return <RNTextInput ref={inputRef} {...props} />;
});

export default TextInput;

Ở đây, TextInput component sử dụng forwardRef để chuyển tiếp ref từ component cha. useImperativeHandle được sử dụng để expose các method focusclear.

Bước 2: Tạo component cha để sử dụng các method của component con

import React, { useRef } from 'react';
import { View, Button } from 'react-native';
import TextInput from './TextInput'; // Đảm bảo import đúng đường dẫn

const ParentComponent = () => {
  const inputRef = useRef(null);

  const focusInput = () => {
    if (inputRef.current) {
      inputRef.current.focus();
    }
  };

  const clearInput = () => {
    if (inputRef.current) {
      inputRef.current.clear();
    }
  };

  return (
    <View>
      <TextInput ref={inputRef} placeholder="Enter text here" />
      <Button title="Focus the input" onPress={focusInput} />
      <Button title="Clear the input" onPress={clearInput} />
    </View>
  );
};

export default ParentComponent;

Giải thích:

  1. Component con (TextInput): Sử dụng forwardRef để nhận ref từ component cha và useImperativeHandle để expose các method focusclear.

  2. Component cha (ParentComponent): Tạo một ref bằng useRef và truyền ref đó vào TextInput. Khi các nút "Focus the input" và "Clear the input" được nhấn, các method tương ứng được gọi thông qua inputRef.current.

Khi nào nên sử dụng useImperativeHandle?

  • Khi cần expose method cụ thể từ component con: Khi bạn muốn component cha có thể gọi một số method cụ thể của component con mà không cần truyền callback qua props.

  • Giữ nguyên tính đóng gói: Khi bạn muốn giữ tính đóng gói của component con, không muốn để lộ chi tiết triển khai ra ngoài.