делать и ненужные создания и назначения обратных вызовов (initialState)?

Я не понимаю, как работает make:

let component = ReasonReact.reducerComponent("Greeting");

let make = (~name, _children) => {
  ...component,
  initialState: () => 0, /* here, state is an `int` */
  render: (self) => {
    let greeting =
      "Hello " ++ name ++ ". You've clicked the button " ++ string_of_int(self.state) ++ " time(s)!";
    <div>{ReasonReact.stringToElement(greeting)}</div>
  }
};

Насколько я понимаю, make будет вызываться каждый раз, когда компонент <Greeting> используется в методе рендеринга родительского компонента, поэтому он будет вызываться несколько раз.

Но это также означает, что запись компонента будет создавать несколько раз функцию initialState, верно?

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

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


person Sebastien Lorber    schedule 20.02.2018    source источник
comment
Если ваше начальное состояние не зависит от ваших исходных реквизитов, вы можете поднять его: let initialState = () => 0; let make = (~name, _children) => { ...component, initialState, render: ... };   -  person Yawar    schedule 22.02.2018
comment
конечно, я могу это сделать, но когда это зависит от начальных реквизитов, я все еще не вижу ценности воссоздания этой функции каждый раз :) на самом деле не ищу обходных путей производительности, но больше понимаю дизайнерское решение, стоящее за этим.   -  person Sebastien Lorber    schedule 22.02.2018
comment
Ах, извините. Таким образом, поскольку make вызывается при каждом рендеринге, initialState необходимо оценивать лениво (то, что вы моделируете как вызов по имени в Scala). Это связано с тем, что его следует оценивать только при первом вызове make, а не после него. Они просто решили использовать для этого функцию. Также можно моделировать как значение OCaml lazy ( caml.inria .fr/pub/docs/manual-ocaml/libref/Lazy.html ), но в скомпилированном выводе получается примерно то же самое.   -  person Yawar    schedule 22.02.2018
comment
Хорошо, поименные параметры Scala со мной разговаривают. Итак, вы говорите, что функция initialState есть, но на самом деле она будет создана только тогда, когда она будет вызвана? Таким образом, можно создать несколько ленивых переменных, но одна из них приведет к созданию функции?   -  person Sebastien Lorber    schedule 22.02.2018
comment
Правильно, я не вижу способа обойти это с текущим дизайном...   -  person Yawar    schedule 22.02.2018


Ответы (1)


Насколько я понимаю, make будет вызываться каждый раз, когда компонент <Greeting> используется в методе рендеринга родительского компонента, поэтому он будет вызываться несколько раз.

Да, make вызывается для каждого рендера.

В этом примере make Inside равно p отображается в консоли каждый раз при нажатии кнопки, что вызывает новый рендеринг компонента Inside.

Мне было любопытно лучше понять, почему это происходит, поэтому поделился этим здесь, если кому-то это покажется интересным. Сегодня реализация работает примерно так:

  • <Greeting name="John" /> преобразуется в ReasonReact.element(Greeting.make(~name="John", [||])) Разумом ppx. Вы можете найти реализацию в основном репозитории Reason.
  • В этом выражении Greeting.make — это функция make, на которую вы ссылаетесь, та же самая функция, которую должен определять каждый компонент ReasonReact.
  • ReasonReact.element это место, где происходит волшебство. Эта функция вызовет createElement следующим образом (источник код):
createElement(
  component.reactClassInternal,
  ~props={"key": key, "ref": ref, "reasonProps": element},
  [||]
)
  • element, переданный в качестве реквизита reasonProps, на самом деле является «шаблоном» полного компонента, который возвращается make (см. назначение несколько строк выше).
  • component.reactClassInternal указывает на компонент React, который я буду называть WiringComponent для простоты. По сути, этот WiringComponent объявляет все методы жизненного цикла React и реализует их, просто делегируя фактическое поведение функциям, которые исходят из «шаблона», объявленного с помощью make. Эти функции выбираются из реквизита reasonProps, который передается этому WiringComponent. Вы можете увидеть реализацию этого компонента здесь< /а>.

Итак, даже если make вызывается один раз для каждого рендеринга, как вы упомянули, на самом деле рендерится что-то вроде <WiringComponent reasonProps=Greeting.make(...) />. Затем, когда вызывается WiringComponent.getInitialState (как обычно, только один раз для экземпляра компонента), он делегирует этот вызов Greeting.initialState.

Но это также означает, что запись компонента будет создавать несколько раз функцию initialState, верно?

Это то, что, кажется, делает код. Решение этого не кажется мне тривиальным. Учитывая, что помеченные аргументы используются для моделирования свойств компонентов, невозможно запомнить make, не отказываясь от безопасности типов, поскольку может быть много «разновидностей» этой функции (по одной для каждого компонента).

person Javier Chávarri    schedule 22.02.2018
comment
спасибо за объяснения. Не знаю, что вы имеете в виду под ароматами. Что я хотел бы понять, так это дизайнерские решения, лежащие в основе этой функции make. Не могли бы мы объявить такие функции, как initialState, в другом месте, чтобы они создавались только один раз для каждого экземпляра компонента? - person Sebastien Lorber; 22.02.2018
comment
Опять же, не участник, так что отнеситесь к этому с недоверием (мне также любопытно узнать, что скажут создатели ReasonReact). Что я имел в виду под ароматами, так это то, что каждый компонент имеет разные типы реквизита. Если мы объединим это с решением реализовать пропсы в качестве помеченных аргументов, то становится очень сложно (невозможно?) иметь запись ReactComponent, содержащую всю эту информацию. Как будет выглядеть тип функции render? - person Javier Chávarri; 22.02.2018
comment
К сожалению, я, возможно, недостаточно знаю Ocaml, чтобы понять, что вы имеете в виду :) постараюсь посмотреть, может ли кто-нибудь помочь мне понять в Discord - person Sebastien Lorber; 22.02.2018
comment
@SebastienLorber, вы можете определить функцию в другом месте и ссылаться на нее внутри компонента. - person thangngoc89; 22.02.2018
comment
это то, что сказал Явар, но это не сработает, если initialState нужно прочитать реквизиты - person Sebastien Lorber; 22.02.2018