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

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

(defn windowdim_comp
 []
 (with-let [wndcomp_state (atom {:text "Parent2Component"})
         resize_handler #(swap! wndcomp_state assoc
                         :height (.-innerHeight js/window)
                         :width (.-innerWidth js/window))
         mousemove_handler #(swap! wndcomp_state assoc
                                   :x (.-pageX %)
                                   :y (.-pageY %))
         _ (.addEventListener js/window "resize" resize_handler)
         _ (.addEventListener js/document "mousemove" mousemove_handler)
         _ (swap! wndcomp_state assoc :height (.-innerHeight js/window))
         _ (swap! wndcomp_state assoc :width (.-innerWidth js/window))]
[:div
 [:p "width : " (:width @wndcomp_state) " height : " (:height @wndcomp_state)]
 [:p "x : " (:x @wndcomp_state) " y : " (:y @wndcomp_state)]
 (svgrender (cursor wndcomp_state [:text :width :height]))
 ]
(finally
  (.removeEventListener js/window "resize" resize_handler)
  (.removeEventListener js/document "mousemove" mousemove_handler))))

Затем этот компонент вызывает дочерний компонент svgrender с курсором, принимающим только значения: text: width и: height ... определенные следующим образом:

(defn svgrender
 [parent_state]
  (with-let [svgstate (atom {:clicked false})
           mousedown_handler #(swap! svgstate assoc
                                     :clicked true)
           mouseup_handler #(swap! svgstate assoc
                                   :clicked false)]
    [:svg {:viewBox "0 0 500 500"
         :width 500
         :height 500
         :id "svgcontainer"
         :onMouseDown mousedown_handler
         :onMouseUp mouseup_handler}
      [:g {:id "layer1"}
       [:rect {:id "rect1"
            :width 500
            :height 500
            :x 0
            :y 0
            :style {:fill (if (:clicked @svgstate)
                            "#ff00ff"
                            "#00ffff")}}]
         [:text {:x 5
            :y 15
            :class "small"}
          (gstring/format "width : %s height : %s" (:width @parent_state) (:height @parent_state))]
         [:text {:x 5
            :y 35
            :class "small"}
          (gstring/format "Click status : %s" (:clicked @svgstate))]
         [:text {:x 5
            :y 55
            :class "small"}
          "Parent Text:  " (:text @parent_state)]]]
        (finally
          (.removeEventListener (.getElementById js/document "svgcontainer")
                          "onmousedown" mousedown_handler)
          (.removeEventListener (.getElementById js/document "svgcontainer")
                          "onmouseup" mouseup_handler))))

Проблема в том, что я делаю, я не могу получить значения из parent_state для отображения в дочернем узле .... они просто отображаются как нули введите описание изображения здесь

Кто-нибудь может мне помочь? Не знаю, что делаю не так !!

ОБНОВЛЕНИЕ: По предложению Уолтона я придумал несколько вариантов

  1. Обращение к родительскому компоненту с переданным полным родительским состоянием

    (svgrender wndcomp_state)

Результат: Неудача

  1. Родительский вызов компонента с курсором, как в исходном коде

    (svgrender (курсор wndcomp_state [: текст: ширина: высота]))

Результат: Неудача

  1. Обращение родителей к компоненту с отслеживанием по предложению Уолтона

    (svgrender (номер дорожки (клавиши выбора @wndcomp_state [: text: width: height])))

Результат: Пройдено !!

  1. Квадратный вызов компонента с переданным полным родительским состоянием

    [svgrender wndcomp_state]

Результат: Пройдено !!

  1. Квадратный вызов компонента с курсором, как в исходном коде

    [svgrender (курсор wndcomp_state [: текст: ширина: высота])]

Результат: Неудача

  1. Квадратный вызов компонента с треком согласно предложению Уолтона

    [svgrender (номер дорожки (клавиши выбора @wndcomp_state [: text: width: height]))]

Результат: Пройдено !!

Это так странно .... нет? Хотя я принимаю ответ Уолтона, может ли кто-нибудь объяснить, почему это так?

2
user3570501 19 Май 2021 в 20:50

1 ответ

Лучший ответ

курсор ведет себя как get-in, а в приведенном выше коде предполагается, что он будет вести себя как select-keys. Хотя вы можете заставить курсор вести себя так, как хотите, передав функцию в качестве первого аргумента (см. Второй пример в документации), вам не нужно писать курсор, поэтому лучше использовать track. Что-то вроде (track #(select-keys @wndcomp_state [:text :width :height])) (непроверено)

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

Однако даже эту проблему можно решить без новой реакции. Просто пройдите по простой карте. Поэтому вместо [svgrender (track #(select-keys @wndcomp_state [:text :width :height]))] у вас просто [svgrender (select-keys @wndcomp_state [:text :width :height])] (примечание: я также использую квадратные скобки вместо скобок, см. здесь ). При использовании этого подхода при изменении wndcomp_state компонент windowdim_comp будет повторно отрисован, что заставит его вызвать компонент svgrender с текстом, шириной и высотой из wndcomp_state. Если они изменились, это похоже на вызов компонента React с новыми реквизитами, он будет повторно отображен. Если у них нет вашего вызова svgrender с теми же аргументами, с которыми он был изначально отрисован. Реагент не будет повторно отображать его в этом сценарии (с использованием квадратных скобок. С скобками он всегда будет повторно отображаться).

Тот же эффект, но меньше работы для зависимостей отслеживания Reagent / React.

2
Walton Hoops 20 Май 2021 в 02:53