Який правильний прототип для посилання в React?


85

Я зберігаю посилання у своєму магазині redux і використовую mapStateToProps, щоб виставити посилання на компоненти, які потребують до нього доступу.

Посилання, яке зберігається, виглядає так:

ref={node => this.myRefToBePutInReduxGlobalStore = node}

Який правильний propType для цього посилання?


8
Оскільки посилання не піддаються серіалізації, не рекомендується розміщувати їх у магазині Redux. Тим не менше, значення, передане refprop, є функцією з першим аргументом, посиланням на елемент DOM або нуль. Це функція .
rishat

5
Ви не можете використовувати propType для refs, але ви можете використовувати його для props. Оскільки ви передаєте його в магазин redux, тоді це представляється як propподібне this.props.myRefToBePutInReduxGlobalStore. myRefToBePutInReduxGlobalStoreмає бути типом вузла, тому PropTypes.nodeмає працювати і для вас
Причина

Я думаю, це повинно бути, PropType.objectтому що ref - це, в основному, екземпляр класу, який є об'єктом. Отже, коли ви передаєте елемент ref як реквізит, це буде тип об’єкта
Hriday Modi

Відповіді:


96

TL; DR

Якщо ви хочете, щоб проп ввести ref, який очікує лише власні елементи DOM, такі як a divабо an input, правильним визначенням є таке:

refProp: PropTypes.oneOfType([
    // Either a function
    PropTypes.func, 
    // Or the instance of a DOM native element (see the note about SSR)
    PropTypes.shape({ current: PropTypes.instanceOf(Element) })
])

Відповідь на конкретне питання, описане в оригінальній публікації

У прикладі запитання OP потрібно оголосити не тип ref prop, а матеріал, на який вказує ref, і який буде передано з redux за допомогою mapStateToProps. Тип опису для елемента DOM буде: myRefToBePutInReduxGlobalStore: PropTypes.instanceOf(Element)(якщо це елемент DOM). Хоча, я б:

  1. Перейменуйте його на myElementToBePutInReduxGlobalStore(елемент не є посиланням)
  2. Уникайте зберігання даних, які не можна серіалізувати, у сховищі redux
  3. Уникайте передачі елементів у реквізит, як пояснив інженер React Себ Маркбідж

Довга відповідь на актуальне питання

З: Який правильний проптип для посилання у React?

Приклад використання:

function FancyInput({ inputRef }) {
    return (
        <div className="fancy">
            Fancy input
            <input ref={inputRef} />
        </div>
    )
}

FancyInput.propTypes = {
    inputRef: ???   // What is the correct prop type here?
}

function App(props) {
    const inputRef = React.useRef()
    useLayoutEffect(function focusWhenStuffChange() {
        inputRef.current?.focus()
    }, [props.stuff])
    return <FancyInput inputRef={inputRef} />
}

Сьогодні в реакції існують два типи посилань:

  1. Об'єкт, який виглядає так:, { current: [something] }як правило, створюється React.createRef()помічником або React.useRef()гачком
  2. Функція зворотного виклику, яка отримає [something]як свій аргумент (як та в прикладі OP)

Примітка: історично, ви також можете використовувати рядок ref, але це вважається застарілим і буде видалено з реакції

Другий елемент досить простий і вимагає наступного типу пропелера: PropTypes.func.

Перший варіант менш очевидний, оскільки, можливо, ви захочете вказати тип елемента, на який вказує посилання.

Багато хороших відповідей

ref може вказувати на щось інше, ніж елемент DOM.

Якщо ви хочете бути абсолютно гнучкими:

refProp: PropTypes.oneOfType([
    PropTypes.func, 
    PropTypes.shape({ current: PropTypes.any })
])

Вищезазначене просто забезпечує форму об'єкта ref з currentвластивістю. Це буде працювати в будь-який час для будь-якого виду. З метою використання типу реквізиту, який є способом виявлення будь-яких невідповідностей під час розвитку , цього, мабуть, достатньо. Це здається дуже малоймовірним , що об'єкт з формою { current: [any] }, і передається на refToForwardопору, було б НЕ бути фактичним вих.

Тим не менш , ви можете заявити, що ваш компонент не очікує будь-якого типу ref, а лише певного типу, враховуючи те, для чого йому потрібен цей ref.

Я налаштував пісочницю, де продемонстрував кілька різних способів оголосити посилання, навіть деякі нетрадиційні, а потім протестував їх із багатьма типами опор. Ви можете знайти його тут .


Якщо ви очікуєте лише посилання, що вказує на власний елемент введення, а не на будь-який HTML Element:

refProp: PropTypes.oneOfType([
    PropTypes.func, 
    PropTypes.shape({ current: PropTypes.instanceOf(HTMLInputElement) })
])

Якщо ви очікуєте лише посилання на компонент класу React:

refProp: PropTypes.oneOfType([
    PropTypes.func, 
    PropTypes.shape({ current: PropTypes.instanceOf(Component) })
])

Якщо ви очікуєте лише посилання, яке вказує на функціональний компонент, використовуючи useImperativeHandleдля викриття якогось загальнодоступного методу:

refProp: PropTypes.oneOfType([
    PropTypes.func, 
    PropTypes.shape({ current: PropTypes.object })
])

Примітка: вищезазначений тип prop цікавий тим, що він також охоплює реакційні компоненти та власні елементи DOM, оскільки всі вони успадковують базовий javascript Object


Немає єдиного хорошого способу оголосити тип опису для посилання, це залежить від використання. Якщо ваш референт вказує на щось дуже ідентифіковане, можливо, варто додати конкретний тип реквізиту. В іншому випадку досить перевірити загальну форму.


Примітка про візуалізацію на стороні сервера

Якщо ваш код повинен працювати на сервері, якщо ви вже не заповнюєте середовище DOM, Elementабо будь-який інший тип на стороні клієнта буде undefinedв NodeJS. Ви можете використовувати наступну підкладку для її підтримки:

Element = typeof Element === 'undefined' ? function(){} : Element

Наступні сторінки React Docs дають більше уявлення про те, як використовувати посилання як на об'єкти, так і на зворотні виклики , а також як використовувати useRefхук

Відповідь оновлена ​​завдяки @Rahul Sagore, @Ferenk Kamra, @svnm та @Kamuela Franco


2
Це Elementне визначається при рендерингу на стороні сервера. Будь-яке рішення цього?
Рахул Сагоре

Гарна думка. А як щодо цього шима: Element = typeof Element === 'undefined' ? function(){} : Element(запропонований Райаном Флоренсом у якомусь випуску github). Якщо це працює для вас, я можу додати це до своєї відповіді.
Пандайоло,

Я зрозумів і додав це if (!isBrowser) { global.Element = null; }. Працював у мене. isBrowser = typeof window !== 'undefined';
Рахул Сагоре

Чудова відповідь. Я хотів би, щоб TL; DR був у верхній частині відповіді.
Камуела Франко

12

Дуже схожий на допис @ Pandaiolo,

PropTypes.elementType тепер додано

forwardedRef: PropTypes.oneOfType([
  PropTypes.func,
  PropTypes.shape({ current: PropTypes.elementType })
]),

Якщо використання PropTypes> = 15.7.0 PropTypes.elementTypeбуло додано 10 лютого 2019 року до цього PR


Дякую @svnm, я оновив свою відповідь, щоб відобразити це, що робить її простішою!
Pandaiolo,

1
Врешті-решт, це elementTypeне спрацювало для мене, тому я фактично протестував купу типів опори на різні типи компонентів, на які можна вказати посиланням, у пісочниці. Ось результати: codesandbox.io/s/loving-benz-w6fbh . Я не впевнений, що я охопив усі можливості, але, схоже, правильним прототипом для елемента DOM є instanceOf(Element)(або object), для класу це instanceOf(Component)(або object) та для конкретного випадку використання функціонального компонента, який useImperativeHandleйого використовує object. Думки?
Pandaiolo,

9

Я перевіряю лише бажаний спосіб, і оскільки PropType.object не дуже цінний, я використав це:

PropTypes.shape({ current: PropTypes.instanceOf(Element) })

1

Я перевірив усі вищезазначені відповіді, але отримую попередження від реакції, тому багато досліджував і отримав правильну відповідь.

import React, { Component } from 'react';

ComponentName.propTypes = {
  reference: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.shape({ current: PropTypes.instanceOf(Component) }),
  ]),
};
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.