Кастомный хук для диспатчинга Redux экшенов
2 года назад·1 мин. на чтение
В этой статье напишем кастомный хук для вызова action creator’а Redux вместе с dispatch() внутри.
Часто, вызывая action creator, мы забываем обернуть его в
Допустим, мы работаем над приложением списка задач, и у нас есть действие
В компоненте вызов будет выглядеть следующим образом.
dispatch()
, а вместо этого вызываем как обычная функцию. Таким образом action не доходит до редьюсера.
Мы создадим собственный хук, в котором будет выполняться обертывание вызова функции вaddProductToCart(product); // не верно dispatch(addProductToCart(product)); // верно
dispatch()
, чтобы мы могли отправлять наши экшены, вызывая их как обычные функции с уже встроенным диспатчингом.
Реализация довольно проста.import { useCallback } from 'react'; import { useDispatch } from 'react-redux'; export const useWithDispatch = (fn) => { const dispatch = useDispatch(); return useCallback( payload => () => dispatch(fn(payload)), [dispatch, fn] ) }
useWithDispatch
— это функция высшего порядка, поскольку она принимает функцию (action creator) в качестве аргумента и возвращает другую функцию, которая при вызове вызовет переданную функцию, обернутую с помощью dispatch()
. Обязательно запоминаем функцию обратного вызова с помощью useCallback
.
addTask()
, которое берет текст задачи и добавляет его в список задач:
// actions.js const addTaskAction = (text) => { return { type: 'ADD_TASK', payload: { id: 'some-id', text } } }
import { addTaskAction } from './actions'; export const Component = () => { const addTask = useWithDispatch(addTaskAction); const handleClick = () => { addTask('Learn Redux'); } // ... }
Итоги
Я надеюсь, что вы нашли этот пост полезным. Вам по-прежнему нужно помнить о передаче создателей действий (action creator) вuseWithDispatch
, но будет легче запомнить, что вы делаете это в самом начале, а не при их вызове.Как вызвать метод дочернего компонента из родительского компонента с помощью 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;