Как вызвать метод дочернего компонента из родительского компонента с помощью useImperativeHandle

2 года назад·3 мин. на чтение

Быстрый старт с useImperativeHandle

В этой статье будет показано, как вызвать метод дочернего компонента с помощью ссылки. Чтобы решить эту проблему, мы будем использовать хуки useRef и useImperativeHandle.

Дочерний компонент

Начнем с простого дочернего компонента, в котором содержится кнопка. Нажатие на кнопку вызывает внутренний метод doSomething.
// Child.jsx

function Child(props, ref) {
  const doSomething = () => {
    console.log("do something");
  };
  return (
    <div>
      <h1>Child Component</h1>
      <button onClick={doSomething}>Run</button>
    </div>
  );
}

export default Child;

Родительский компонент

Далее рассмотрим родительский компонент. В нем используется дочерний компонент, описанный выше. Обратите внимание, что в родительском компоненте есть собственная кнопка сохранения.
// App.jsx

import Child from "./Child";

function App() {
  const save = () => {};
  return (
    <div>
      <Child />
      <button onClick={save}>Save</button>
    </div>
  );
}

export default App;

Хук useImperativeHandle

Теперь давайте сосредоточимся на нашей задаче. Мы хотим вызвать метод (doSomething) дочернего компонента при нажатии кнопки (Save) из родительского компонента. Чтобы вызвать метод из дочернего компонента, нам нужно сначала выставить его наружу. useImperativeHandle определяет значение объекта, которое предоставляется родительскому компоненту при использовании ref. Добавляя наш метод к этому объекту, мы делаем его доступным в родительских компонентах.
// Child.jsx

import { useImperativeHandle } from "react";

function Child(props, ref) {
  const doSomething = () => {
    console.log("do something");
  };

  useImperativeHandle(ref, () => ({ doSomething }));

  return (
    <div>
      <h1>Child Component</h1>
      <button onClick={doSomething}>Run</button>
    </div>
  );
}
export default Child;
useImperativeHandle следует использовать с forwardRef.
forwardRef позволяет родительскому компоненту передавать ссылки своим дочерним элементам. Чтобы прикрепить функции или поля к этой ссылке (к рефу), используется хук useImperativeHandle.
// Child.jsx

import { forwardRef, useImperativeHandle } from "react";

function Child(props, ref) {
  const doSomething = () => {
    console.log("do something");
  };

  useImperativeHandle(ref, () => ({ doSomething }));

  return (
    <div>
      <h1>Child Component</h1>
      <button onClick={doSomething}>Run</button>
    </div>
  );
}

export default forwardRef(Child); // Child обернут в forwardRef
На этом этапе мы можем создать ссылку в родительском компоненте с помощью хука useRef и передать ее дочернему компоненту. Получив эту ссылку, мы можем вызвать метод doSomething дочернего компонента.
// App.jsx

import { useRef } from "react";
import Child from "./Child";

function App() {
  const childRef = useRef(null);

  const save = () => {
    if (childRef.current) {
      childRef.current.doSomething();
    }
  };

  return (
    <div>
      <Child ref={childRef} />
      <button onClick={save}>Save</button>
    </div>
  );
}

export default App;

Добавим TypeScript

Далее посмотрим, какие изменения нужно сделать, чтобы вызвать тот же дочерний метод из родительского компонента при использовании TypeScript. Во-первых, нам нужно определить новый интерфейс, содержащий метод, который будет представлен.
export interface RefType {
  doSomething: () => void;
}
Затем новый тип (RefType) используется при получении ссылки в дочернем компоненте.
function Child(props: PropsType, ref: Ref<RefType>)
Ниже приведен полный код дочернего компонента.
// Child.jsx

import { forwardRef, useImperativeHandle, Ref } from "react";

export interface PropsType {}
export interface RefType {
  doSomething: () => void;
}

function Child(props: PropsType, ref: Ref<RefType>) {
  const doSomething = () => {
    console.log("do something");
  };
  useImperativeHandle(ref, () => ({ doSomething }));
  return (
    <div>
      <h1>Child Component</h1>
      <button onClick={doSomething}>Run</button>
    </div>
  );
}

export default forwardRef(Child);
В родительский компонент нам нужно импортировать этот RefType, содержащий все публичные дочерние методы, и использовать его при создании ref.
// App.jsx

import Child, { RefType } from "./Child";
//...
const childRef = useRef<RefType>(null);
Полный код родительского компонента.
import { useRef } from "react";
import Child, { RefType } from "./Child";

function App() {
  const childRef = useRef<RefType>(null);

  const save = () => {
    if (childRef.current) {
      childRef.current.doSomething();
    }
  };

  return (
    <div>
      <Child ref={childRef} />
      <button onClick={save}>Save</button>
    </div>
  );
}

export default App;

Полное руководство по React Router v6. Часть 4 - Подробно о роутерах

2 года назад·3 мин. на чтение

В этой статье рассмотрим все виды роутеров из библиотеки React Router v6 - BrowserRouter, NativeRouter, HashRouter, HistoryRouter, MemoryRouter, StaticRouter.

В первой части мы говорили о настройке маршрутизатора и упоминали BrowserRouter и NativeRouter, но это не единственные типы роутеров. Всего существует 6 роутеров, и в этой части мы подробно рассмотрим каждый из них. Серия статей о React Router v6 состоит из 4 частей.
  1. Основы React Router
  2. Продвинутые определения маршрутов
  3. Управление навигацией
  4. Подробно о роутерах (рассматривается в этой статье)

BrowserRouter

Из первой части мы уже знакомы с BrowserRouter. Это роутер по умолчанию, который вы должны использовать, если вы работаете над веб-приложением, и это роутер, который вы будете использовать в 99% всех своих приложений, поскольку он охватывает все обычные варианты использования маршрутизации. Другие роутеры, о которых поговорим далее, имеют очень специфические варианты использования.

NativeRouter

NativeRouter по сути является эквивалентом BrowserRouter, но для React Native. Если вы используете React Native, то это роутер, который вы захотите использовать.

HashRouter

Этот роутер работает очень похоже на BrowserRouter, но основное отличие заключается в том, что вместо того, чтобы изменять URL-адрес на что-то вроде http://localhost:3000/books он будет хранить URL-адрес в хэше, как http://localhost:3000/#/books. Как видите, этот URL-адрес имеет # после URL-адреса, который представляет собой хэш-часть URL-адреса. Все, что находится в хэш-части URL-адреса, является просто дополнительной информацией, которая обычно обозначает идентификатор на странице для целей прокрутки, поскольку страница будет автоматически прокручиваться до элемента с идентификатором, представленным хешем, при загрузке страницы. В React Router этот хэш на самом деле не используется для хранения идентификационной информации для прокрутки, а вместо этого он хранит информацию, связанную с текущим URL-адресом. Причина, по которой React Router делает это, заключается в том, что некоторые хостинг-провайдеры не позволяют вам фактически изменять URL-адрес вашей страницы. В этих очень редких случаях вы захотите использовать HashRouter, поскольку HashRouter не изменит фактический URL-адрес вашей страницы, а изменит только хэш вашей страницы. Если вы можете использовать какой-либо URL-адрес у своего хостинг-провайдера, то это не то, что вам следует использовать.

HistoryRouter

HistoryRouter (в настоящее время называется unstable_HistoryRouter) — это роутер, который позволяет вручную управлять объектом истории, который React Router использует для хранения всей информации, связанной с историей маршрутизации вашего приложения. Этот объект истории помогает убедиться, что такие вещи, как кнопки «Назад» и «Вперед» в браузере, работают правильно. Это роутер, который вы, вероятно, никогда не должны использовать, если у вас нет очень конкретной причины, по которой вы хотите перезаписать или контролировать поведение истории по умолчанию React Router.

MemoryRouter

MemoryRouter немного отличается от остальных роутеров, о которых мы говорили, поскольку вместо того, чтобы хранить информацию о текущем маршруте в URL-адресе браузера, этот роутер хранит информацию о роуте непосредственно в памяти. Очевидно, что это очень неподходящий роутер для обычных операций маршрутизации, но этот роутер невероятно полезен, когда вы пишете тесты для своего приложения, у которого нет доступа к браузеру. Из-за того, как работает React Router, вам необходимо, чтобы ваши компоненты были упакованы в маршрутизатор, иначе весь ваш код маршрутизации будет выдавать ошибки и ломаться. Это означает, что даже если вы хотите протестировать один компонент, вам нужно будет обернуть этот компонент внутрь маршрутизатора, иначе он будет выдавать ошибки. Если вы тестируете свой код таким образом, что у него нет доступа к браузеру (например, модульное тестирование), то маршрутизаторы, о которых мы говорили до сих пор, будут выдавать ошибки, поскольку все они зависят от URL-адреса в браузере. MemoryRouter, с другой стороны, хранит всю свою информацию в памяти, что означает, что он никогда не обращается к браузеру и идеально подходит при модульном тестировании компонентов. Однако, за исключением этого конкретного случая использования, этот маршрутизатор никогда не будет использоваться.

StaticRouter

Роутер StaticRouter также имеет очень специфический вариант использования. Этот маршрутизатор специально предназначен для серверного рендеринга ваших приложений React, поскольку он принимает один проп location и визуализирует ваше приложение, используя этот location в качестве URL-адреса. Этот роутер на самом деле не может выполнять какую-либо маршрутизацию и будет просто отображать одну статическую страницу, но это идеально подходит для рендеринга на сервере, поскольку вы хотите просто отобразить HTML вашего приложения на сервере, а затем клиент может настроить всю вашу маршрутизацию и так далее.
<StaticRouter location="/books">
  <App />
</StaticRouter>

Итоги

React Router — это огромная библиотека с множеством удивительных функций, поэтому она является популярной библиотекой для маршрутизации. Очень интересна реализация вложенных маршрутов, поскольку это значительно упрощает создание интуитивно понятных маршрутов.