Как передать новые реквизиты в состояние компонента в Reagent?

У меня есть компонент:

(defn inner-input [cljs_element activeEl title]
  (let [form (atom title)]
    (fn [cljs_element activeEl title]
      [:input {:type "text"
           :onChange #(reset! form (.. % -target -value))
           :on-blur #(change-title cljs_element (.. % -target -value))
           :style {:display (if (:active (:node cljs_element)) "block" "none")
                   :width (* (+ 1 (count @form)) 8)
                   :max-width 730
                   :min-width 170}
           :value @form}])))

Он вложен в другой компонент:

(defn card-input [cljs_element activeEl]
  (fn [cljs_element activeEl]
    (let [title (:title (:node cljs_element))]
      [:div
        [inner-input cljs_element activeEl title]])))

Когда я ввожу данные на вход в компоненте inner-input, мне нужно обновить форму локального состояния. И когда внешний компонент card-input обновляется, я хочу сбросить свою форму на новую опору title из аргумента. Как я могу этого добиться?

Я попытался поставить (reset! form title) между let и fn в компоненте inner-input, но это не поможет


person Vyguzov Aleksandr    schedule 19.12.2017    source источник
comment
насколько я помню, вам нужно обновить атом form в функции рендеринга, он будет вызываться после каждого вызова рендеринга из внешнего компонента. Вам просто нужно добавить (reset! form title) сразу после (fn [cljs_element activeEl title] ...   -  person leetwinski    schedule 19.12.2017
comment
@leetwinski, но в этом случае (reset! form title) перепишет мое локальное состояние, измененное на: onChange. Мне нужно изменить атом формы на заголовок из аргументов только тогда, когда внешний компонент был обновлен   -  person Vyguzov Aleksandr    schedule 19.12.2017
comment
ну, ваша функция рендеринга (сброс) будет вызываться только в случае обновления внешнего компонента. После этого данные будут контролироваться вашим обработчиком изменений.   -  person leetwinski    schedule 19.12.2017
comment
Если вы пойдете по пути (reset! form title) (внутри внутренней fn), вам также придется поместить let за пределы внешней fn и превратить ее в ratom. Например, (let [title (atom (:title (:node cljs_element)))] (fn [cljs_element activeEl] ...))   -  person Erwin Rooijakkers    schedule 27.12.2017


Ответы (1)


Вы можете использовать reagent/track! для прослушивания изменений заголовка и reagent/dispose для прекращения прослушивания. В качестве альтернативы вы можете использовать add-watch и remove-watch, но трек - более удобный синтаксис.

(defn inner-input [title]
  (reagent/with-let
    [form (reagent/atom @title)
     watch (reagent/track! (fn [] (reset! form @title)))]
    [:label
     "Inner input"
     [:input {:on-change (fn [e]
                           (reset! form (.. e -target -value)))
              :on-blur (fn [e]
                         (reset! title (.. e -target -value)))
              :value @form}]]
    (finally
      (reagent/dispose! watch))))

(defn card-input []
  (reagent/with-let
    [title (reagent/atom "hello")]
    [:div
     [:label "Title"
      [:input {:on-change (fn [e]
                            (reset! title (.. e -target -value)))
               :value @title}]]
     [inner-input title]]))

Теперь, если вы введете внутренний ввод, он обновит только внешний, когда вы выйдете из поля ввода, но изменение внешнего заголовка немедленно изменит внутренний. Вы этого хотели?

Но если вы не хотите передавать заголовок как ratom и должны передавать его как значение, то вместо этого вы можете сравнить его с предыдущим значением, чтобы определить, изменилось ли оно, и сбрасывать форму только тогда, когда оно изменяется.

(when (not= @previous-title title)
  (do (reset! previous-title title)
      (reset! form title)))

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

person Timothy Pratley    schedule 12.01.2018
comment
Спасибо! Это именно то, что я хочу. Прежде чем я прочитал ваш ответ, я сделал это с помощью макроса reagent/create-class. Я написал код, аналогичный второму варианту в вашем ответе, с ключом :component-did-update, функция обратного вызова под этим ключом имеет предыдущие реквизиты в аргументах - person Vyguzov Aleksandr; 19.01.2018