2016-12-06 3 views
0

Я пытаюсь понять @trepidacious'sscalajs-reactwrapper для this HOC компонент реакции.Объяснение для упаковки React HOC-компоненты с scalajs-реагированием

1a) Почему тип обернутого компонента hereReactComponentC[P,_,_,_]?

1b) Почему возвращается тип компонента ReactComponentU_?

def wrap[P](wrappedComponent: ReactComponentC[P,_,_,_]): Props => P => ReactComponentU_ = { 

2) Почему функция завод перешел к SortableElementhere?

val componentFactoryFunction = js.Dynamic.global.SortableElement(wrappedComponent.factory) ? 

Не принимает SortableElement Компонент?

3) Почему обернутые реквизиты прошли как this?

"v" -> wrappedProps.asInstanceOf[js.Any] 

Какова причина этой линии?

Куда это магические v исходит от? Это от scalajs-react или от react-sortable-hoc?

4) В чем причина этой обертки? Если я хочу написать обертку для другого компонента HOC, каков должен быть общий прием для этого?

ответ

3

Здесь очень много частей, но я собрал некоторые ссылки, работающие от самого низкого уровня до самого высокого, при освещении вопросов.

Первыми и наиболее важными определениями являются компоненты React и элементы реагирования. This page содержит подробное объяснение. Я рекомендую полностью пропустить раздел «Управление экземплярами», поскольку он смешивает воды, описывая традиционную модель пользовательского интерфейса при использовании терминов по-разному так, как они используются в React. В целом я понимаю, что:

Компонент React - это общая концепция, которая может быть реализована несколькими способами. Однако он реализован, по сути, это функция из реквизита (и необязательно) для содержимого страницы - средство визуализации.

A React Element - это описание содержимого страницы, представляющее конкретный рендеринг компонента.

React components and props docs Опишите два способа определения компонента React, первый из которых - это функция от реквизита к реагирующему элементу, это тот, который нас интересует. Документы React.createFactory затем подтверждают, что мы можем передать такую ​​функцию для созданияFactory. Насколько я могу судить, это существует для того, чтобы адаптироваться от множества способов определения компонента React (путем подкласса React.Component или React.PureComponent, используя React.createClass или с помощью функции от Props to ReactElement) до способа рендеринга реквизита для ReactElement. Мы можем увидеть что-то об этом, посмотрев на это gist, представляя React.createFactory в React 0.12 - по существу они хотели ввести некоторую абстракцию между классом, используемым для определения компонента React и возможной функцией из реквизита для React Elements, который используется при рендеринге, а не просто позволить классу оказывать реквизиты напрямую.

Далее у нас есть небольшая морщина - React.createFactory помечен как наследие в документах. К счастью, это не является серьезной проблемой, опять же, насколько я могу судить, React.createFactory(type) просто производит функцию f(props), которая идентична React.createElement(type, props) - мы просто фиксируем аргумент type в React.createElement. Я испытал это в реакции трёхсортном-специальной оболочкой, и мы можем использовать createElement вместо createFactory:

val componentFunction = js.Dynamic.global.SortableContainer(wrappedComponent.factory) 
(props) => (wrappedProps) => { 
    val p = props.toJS 
    p.updateDynamic("v")(wrappedProps.asInstanceOf[js.Any]) 
    React.asInstanceOf[js.Dynamic].createElement(componentFunction, p).asInstanceOf[ReactComponentU_] 
} 

Мы теперь почти на вопрос 2). Если мы посмотрим на the source for SortableElement, мы увидим, что функция sortableElement принимает аргумент WrappedComponent - это используется для создания другого компонента React, с помощью подхода подкласса React.Component. В функции render этого класса видно, что WrappedComponent используется как компонент React, поэтому мы знаем, что это действительно компонент, даже без статического типа :) Это означает, что WrappedComponent должен быть чем-то принят React.createElement, поскольку это то, что JSX component use desugars to.

Поэтому мы знаем, что нам нужно передать функции sortableElement что-то, что можно использовать в качестве компонента React в функции javascript React.createElement. Глядя на scalajs-react types doc, мы видим, что ReactComponentC выглядит как хорошая ставка - он конструирует компоненты, предположительно из реквизита. Смотря at the source for this, мы видим, что у нас есть два перспективных значения - reactClass и factory. На этом этапе я понимаю, что код, вероятно, использует неправильный - я пробовал заменить .factory на .reactClass, и это все еще работает, но имеет гораздо больше смысла, поскольку у нас есть комментарий, чтобы сообщить нам, что он дает Output of [[React.createClass()]], который является одним из варианты действительного компонента React. Я подозреваю, что factory также работает, по существу, обертывая предоставленный компонент в createFactory дважды, поскольку вывод createFactory также можно использовать в качестве его ввода ... Я думаю, что с учетом этой поправки мы ответили на вопрос 2 :) Это также в значительной степени отвечает на вопрос 1a) - ReactComponentC - это признак scala, который дает нам .reactClass val, который нам нужен для Scala-defined React Component. Мы только заботимся о типе реквизита, который он использует (поскольку мы должны их предоставить), следовательно, P. Поскольку scala IS typed мы знаем, что это то, что мы получаем от создания Scala React Component в обычном режиме (по крайней мере для компонентов, которые я пробовал).

На вопрос 1b), я нашел тип ReactComponentU_ из кода вроде ReactCssTransitionGroup фасада в scalajs-react Addons и scalajs-react-components notes on interop, который показывает упаковку компонента не-HOC. Посмотрев на сам тип, мы видим, что он расширяет ReactElement, что имеет смысл - это ожидаемый результат рендеринга компонента React.В нашей функции wrap на фасадах SortableElement и SortableContainer мы производим (в конечном итоге) еще одну функцию от реквизита до ReactElement, только один, который перескакивает через несколько обручей, чтобы добраться туда с помощью HOC-подхода. Я не уверен, почему ReactComponentU_ используется вместо ReactElement. Я думаю, что это связано с отслеживанием состояния компонентов через тип, но код все еще компилируется, если я возвращаю ReactElement, что является нечетным.

Вопрос 3) намного проще - scalajs-реагирует на работу с реквизитами, которые могут быть Ints, Longs и т. Д., Но в Javascript это не объекты. Чтобы каждый реквизит компонента scalajs был объектом, scalajs-react обертывает их в объект, например {"v": props}, а затем снова распаковывается, когда они используются. Когда мы завершаем компонент Scala React с помощью HOC, нам нужно каким-то образом получить реквизиты завернутого компонента. Компонент, созданный функцией HOC (компонент «wrapping», SortableElement или SortableContainer) делает это, ожидая, что его собственные реквизиты уже будут содержать реквизиты обернутого компонента в качестве полей, а затем просто перейдут к обернутому компоненту, например в SortableElement визуализирует:

<WrappedComponent 
       ref={ref} 
       {...omit(this.props, 'collection', 'disabled', 'index')} 
      /> 

this.props передается через обернутую компоненту. Поскольку для обернутого компонента scala требуется поле «v» с объектом реквизита scala, нам нужно добавить это в реквизит компонента обертки. К счастью, это поле будет проходить без изменений, которое будет интерпретироваться позже компонентом scala. Вы не увидите поле «v», так как scalajs-response разворачивает его для вас.

Это вызывает проблемы при упаковке некоторых других HOC - например, ReactGridLayout's WidthProvider измеряет ширину упакованного компонента и передает его в реквизитах как {"width": width}, но, к сожалению, мы не можем видеть это из scala. Для этого вполне может быть обходной путь.

Это покрывает деталь и ссылки на части HOC упаковки, но на самом деле этот процесс является довольно легко (при условии, вы не хотите, чтобы получить доступ к реквизиту «инъекционный» в обернутом компонент):

  1. Сделайте объект scala для фасада, чтобы организовать код.
  2. Определите, какие реквизиты необходимы компоненту обертки. Например, в SortableElement это index, collection и disabled. Создайте класс case of thePosts с этими полями в объекте фасада.
  3. Напишите функцию «обертывания» в объекте фасада. Это делает следующее:
  4. Принимает wrappedComponent: ReactComponentC[P,_,_,_] и передает его функции havascript HOC (например, SortableElement) для создания нового компонента React.
  5. Создает объект реквизита javascript с реквизитами компонента обертки и магическим полем «v» с реквизитами обернутого компонента.
  6. Использует функцию react.createElement javascript для создания ReactElement, который отображает завернутый компонент, и отбрасывает это на ReactComponentU_.

Обратите внимание, что на этапе 5 нам нужно преобразовать из нашего класса Scala Props (реквизита компонента оболочки) в простой объект javascript, который может быть понят HOC. Реквизиты обернутого компонента просто идут прямо в поле «v» без преобразования, просто отбрасывая до js.Any.

Код, который я написал для SortableElement и SortableContainer, немного расщепляется, так что wrap возвращает функцию curried, которая принимает реквизиты для компонента-обертки и создает другую функцию из обернутых реквизитов в конечный элемент React. Это означает, что вы можете предоставить реквизиты обертки один раз, а затем использовать результирующую функцию, как обычный компонент в вашем коде рендеринга Scala.

Я обновил SortableElement facade с улучшениями выше, и это в значительной степени минимальный пример HOC-фасада. Я бы предположил, что другие HOC будут очень похожи. Вероятно, вы могли бы отвлечь часть кода для DRY, но на самом деле там действительно не так много.

Благодарим за вопросы и за помощь в этом, посмотрев назад процесс, и особенно ваш вопрос на .factory оставил меня более уверенным в том, что теперь он работает правильно (с описанными изменениями).

3

У меня нет всех ответов, но я понимаю, что автор scalajs-react использует множество типов для предотвращения ошибок при построении компонентов, а также в течение жизненного цикла компонентов, когда-то построенных. Он использует соглашения об именах с суффиксами и буквами для разделения типов, которые имеют смысл, но могут быть сложными вначале.

1a) ReactComponentC является конструктором для компонента (C, как в конструкторе).

1b) ReactComponentU_ представляет собой размонтированный родной (JavaScript) компонент React.

3) Я думаю, глядя на источник scalajs-react, да, «v» - это имя волшебного ключа. Существует (был?) И некоторые примечания в исходном коде в том, что это не является идеальным;)

Существует а plan to simplifyscalajs-react «s типы в new version.

Смежные вопросы