【问题标题】:clojurescript + reagent issueclojurescript + 试剂问题
【发布时间】:2015-01-05 12:13:06
【问题描述】:

我正在使用 clojurescript 和试剂开发一个简单的网络应用程序。我想创建一个简单的“选项卡”组件,它将包含(对于初学者)一个文本输入组件。

该应用有 2 个选项卡,用户可以选择一个选项卡,我想“保留”这两个选项卡中的每个选项卡中的值。

代码如下:

(defn atom-input [value]
  [:input {:type "text"
           :value @value
           :on-change #(reset! value (-> % .-target .-value))}])

(defn simple-tab [index]
  (let [pg-index (atom 1)
        a (atom 0)]
    (fn []
    [:div
     [:h4 (str "index: " @index)]
     [atom-input a]])))

(defn main-page []
  (let [index (atom 0)]
    [:div.container
     [:div.row
      [:button {:on-click (fn [] (reset! index 0))} "select tab 1"]
      [:button {:on-click (fn [] (reset! index 1))} "select tab 2"]]
     [:div.row
      [simple-tab index]]]))

(defn ^:export run []
  (reagent/render-component
   (fn [] [main-page])
   (.-body js/document)))

问题是当我切换选项卡时,组件共享输入字段的值 - 我在这里做错了什么?

非常感谢您的帮助!

【问题讨论】:

    标签: clojure clojurescript reagent


    【解决方案1】:

    问题是您将a (atom 0) 传递给atom-input 控件:[atom-input a]。 这导致您的选项卡之间共享相同的原子值。

    如果您不想共享该值,则需要将 a 更改为地图:a (atom {}) 并将地图和索引传递给 atom-input,例如:

    (defn atom-input [value index]
      [:input {:type "text"
               :value (or (get @value index) "")
               :on-change #(swap! value assoc index (-> % .-target .-value))}])
    
    (defn simple-tab [index]
      (let [pg-index (atom 1)
            a (atom {})]
        (fn []
          [:div
           [:h4 (str "index: " @index)]
           [atom-input a @index]])))
    

    恕我直言,更好的方法是使用光标,这样您就不需要将索引和整个地图传递给atom-input,例如:

    (defn atom-input [value]
      [:input {:type "text"
               :value (or @value "")
               :on-change #(reset! value (-> % .-target .-value))}])
    
    (defn simple-tab [index]
      (let [pg-index (atom 1)
            a (atom {})]
        (fn []
          [:div
           [:h4 (str "index: " @index)]
           [atom-input (reagent/cursor [@index] a)]])))
    

    【讨论】:

      【解决方案2】:

      我认为这里存在一些问题,因为您正在混淆应用程序数据(状态)和显示逻辑数据(即 DOM)。如果你保持两件事不同,即在一个原子中保持应用程序状态,在另一个原子中保持与组件显示相关的数据,那么事情可能会更清晰一些。

      您的 simple-tab 组件不需要知道任何有关选项卡状态的信息。它只需要了解应用程序状态,即通过 atom-input 输入/存储的值。因此,与其传递索引,不如传递你希望它使用的原子。这将需要一些更高级别的逻辑来确定调用。例如,如果您有多个选项卡,您可能会有类似

      (condp = @index
        0 [simple-tab tab0-atom]
        1 [simple-tab tab1-atom]
        ...
        n [simple-tab tabn-atom])
      

      或者您可以修改 simple-tab 以便传入的值(即索引值)用作进入应用程序状态的键 - 我认为游标是最简单的,即

      (def app-state (r/atom {:tabs {0 nil 1 nil}}})
      
      (defn simple-tab [index]
        (let [val-cur (r/cursor app-state [:tabs index])]
          [atom-input val-cur]))
      

      【讨论】:

        【解决方案3】:

        您使用的是form-2组件,即返回函数的组件。

        更多细节在这里:https://github.com/Day8/re-frame/wiki/Creating-Reagent-Components#form-2--a-function-returning-a-function

        这样做时,只会调用返回的函数,因此您的 atom-inputs 共享同一个 atom。

        另外,你应该在内部函数中使用相同的参数

         (defn simple-tab [index]
              (let [pg-index (atom 1)
                    a (atom {})]
                (fn [index]
                  [:div
                   [:h4 (str "index: " @index)]
                   [atom-input a @index]])))
        

        在您的情况下,您传递的是一个原子,因此这并不重要,但如果您忘记了这一点,将来可能会出错。

        关于更广泛的架构,我建议您使用单个全局原子。尝试在这个原子中拥有尽可能多的状态并避免组件本地状态,这样更容易推理。

        您还可以将标签命名为 :product :users 并使用多种方法根据所选标签呈现正确的标签。这更易于阅读,并且将来更易于添加新标签。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多