Каков правильный proptype для ссылки в React?

87

Я храню ссылку в моем хранилище redux и использую mapStateToProps, чтобы предоставить ссылку для компонентов, которым нужен доступ к ней.

Сохраненная ссылка выглядит так:

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

Каков правильный propType для этой ссылки?

danday74
источник
8
Поскольку ссылки не сериализуемы, не рекомендуется помещать их в магазин Redux. Тем не менее, значение, переданное в refprop, является функцией с первым аргументом, ссылкой на элемент DOM или null. Это функция .
Ришат
5
Вы не можете использовать propType для refs, но можете использовать для props. Поскольку вы передаете его в магазин redux, он представляется как propподобный this.props.myRefToBePutInReduxGlobalStore. myRefToBePutInReduxGlobalStoreдолжен быть типом узла, поэтому PropTypes.nodeдолжен работать на вас
Причина
Я думаю, это должно быть, PropType.objectпотому что ref - это в основном экземпляр класса, который является объектом. Следовательно, когда вы передаете элемент ref в качестве реквизита, он будет иметь тип объекта
Хридай Моди

Ответы:

99

TL; DR

Если вы хотите указать ссылку, которая ожидает только нативные элементы 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, а материал, на который указывает ссылка, который будет передан из redux using mapStateToProps. Тип свойства для элемента DOM будет: myRefToBePutInReduxGlobalStore: PropTypes.instanceOf(Element)(если это элемент DOM). Хотя я бы:

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

Длинный ответ на актуальный вопрос

В: Каков правильный proptype для ссылки в 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} />
}

Сегодня в React существует два вида ссылок:

  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 w / currentproperty. Он будет работать в любое время для любого реф. Для использования типа опоры, который является способом выявить любые несоответствия при разработке , этого, вероятно, будет достаточно. Кажется очень маловероятным, что объект с формой { current: [any] }, переданный в refToForwardопору, не будет действительной ссылкой.

Тем не менее , вы можете объявить, что ваш компонент не ожидает какой- либо ссылки, а только определенного типа, учитывая, для чего ему нужна эта ссылка.

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


Если вы только ожидать реф указывающий на родной элемент ввода, а не какой - либо 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 })
])

Примечание: указанный выше тип опоры интересен, потому что он также охватывает компоненты реакции и собственные элементы 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). Если это сработает для вас, я могу добавить это к своему ответу.
Pandaiolo
Я разобрался и добавил это 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
источник
Спасибо @svnm, я обновил свой ответ, чтобы отразить это, что делает его проще!
Pandaiolo 02
1
В конце концов, это elementTypeне сработало для меня, поэтому я фактически протестировал кучу типов опор на различных типах компонентов, на которые может указывать ссылка, в песочнице. Вот результаты: codesandbox.io/s/loving-benz-w6fbh . Я не уверен, что рассмотрел все возможности, но кажется, что правильный тип свойства для элемента DOM - instanceOf(Element)(или object), для класса - instanceOf(Component)(или object), а для конкретного варианта использования функционального компонента, который useImperativeHandleего использует object. Мысли?
Пандайоло
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) }),
  ]),
};
Нишарг Шах
источник