当将原子作为函数参数传递时,试剂的性能问题

3
我使用Clojurescript re-frame和reagent在react-native应用程序上工作。我有一个文本输入组件,并且有两个版本的代码: 版本1:输入文本是一个独立的组件,状态原子作为参数传递,与reagent库文档和示例中推荐的相同。
(defn todo-input [value]
  [rn/text-input
   {:style          (styles :textInput) :multiline true
    :placeholder    "What do you want to do today?" :placeholder-text-color "#abbabb"
    :value          @value
    :on-change-text #(reset! value %)}]
  )

(defn todo-screen []
  (let [value (r/atom nil)]
    [rn/view {:style (styles :container)}
     [rn/text {:style (styles :header)} "Todo List"]
     [rn/view {:style (styles :textInputContainer)}
      [todo-input value]
      [rn/touchable-opacity
       {:on-press (fn [] (rf/dispatch [:add-todo @value]) (reset! value nil))}
       [icon {:name "plus" :size 30 :color "blue" :style {:margin-left 15}}]]]
     [todos]
     ]))

版本2:所有内容都在一个组件中。

(defn todo-screen []
  (let [value (r/atom nil)]
    [rn/view {:style (styles :container)}
     [rn/text {:style (styles :header)} "Todo List"]
     [rn/view {:style (styles :textInputContainer)}
      [rn/text-input
       {:style          (styles :textInput) :multiline true
        :placeholder    "What do you want to do today?" :placeholder-text-color "#abbabb"
        :value          @value
        :on-change-text #(reset! value %)}]
      [rn/touchable-opacity
       {:on-press (fn [] (rf/dispatch [:add-todo @value]) (reset! value nil))}
       [icon {:name "plus" :size 30 :color "blue" :style {:margin-left 15}}]
       ]]
     [todos]]))

问题在于第一个版本在打字时存在性能问题,因为在尝试快速输入时会出现大的延迟和闪烁。版本2没有任何问题,我可以尽可能地快速输入。
根据reagent文档,将r/atom作为参数传递不应该产生任何性能问题。
我在这里做错了什么?避免性能损失的最佳方法是什么?
这只是一个小例子,将一个组件拆分成多个较小的组件是一种良好的实践。此处的状态是局部组件,我不想使用re-frame。
3个回答

2
你的两个版本都有问题。当使用本地状态时,应该使用Form-2类型的组件。像这样:
(defn todo-screen []
  (let [value (r/atom nil)]
    (fn []
      [rn/view {:style (styles :container)}
       [rn/text {:style (styles :header)} "Todo List"]
       [rn/view {:style (styles :textInputContainer)}
        [todo-input value]
        [rn/touchable-opacity
         {:on-press (fn [] (rf/dispatch [:add-todo @value]) (reset! value nil))}
         [icon {:name "plus" :size 30 :color "blue" :style {:margin-left 15}}]]]
       [todos]])))

在这里了解有关Form-2的更多信息。

或者您可以使用r/with-let在这里了解有关with-let的更多信息。

关于您最初的问题,您可以在两个版本之间达成妥协,并将输入和提交按钮提取到单独的组件中:

(defn todo-input-container [on-press]
  (r/with-let [value (r/atom nil)]
    [rn/view {:style (styles :textInputContainer)}
     [rn/text-input
      {:style          (styles :textInput) :multiline true
       :placeholder    "What do you want to do today?" :placeholder-text-color "#abbabb"
       :value          @value
       :on-change-text #(reset! value %)}]

     [rn/touchable-opacity
      {:on-press (fn []
                   (on-press @value)
                   (reset! value nil))}
      [icon {:name "plus" :size 30 :color "blue" :style {:margin-left 15}}]]]))


(defn todo-screen []
  [rn/view {:style (styles :container)}
   [rn/text {:style (styles :header)} "Todo List"]
   [todo-input-container (fn [value] (rf/dispatch [:add-todo value]))]
   [todos]])


谢谢@dumitru。我尝试了with-let和form-2版本,但在输入组件中输入文本时仍然出现闪烁问题。 - Marko

1

re-frame/dispatch将您的事件放入队列中,以供re-frame处理,因此在实际生效之前可能会有一些延迟。

听起来你正在经历与此处所述相同的问题:https://github.com/day8/re-frame/issues/368

因此,一个解决方法是使用re-frame.core/dispatch-sync,它强制re-frame直接且同步地处理事件。您可能还需要添加对reagent.core/flush的调用,以强制重新渲染组件。在构建Web客户端时,我以前没有需要使用flush,但React Native似乎工作方式不同。

您可以在此处阅读有关这两个函数的更多信息:

在上面提到的问题中,https://github.com/Day8/re-com 也被提到,它据说可以以某种方式解决这个问题,但我没有仔细看过。
你的第二个解决方案也并没有错,它只是给你提供了另一种工作方式。因此,如果你需要在每次按键时更新应用程序数据库中的数据,那么只有类似 #1 的东西才能起作用。或者使用解决方案 #2,但将原子作为参数传递给组件。

0
看起来问题在于reagent不太支持具有受控输入的组件,因为它是异步的。
应该避免使用受控输入(通过:value),或者在更改:value后立即强制更新组件。
有关更多详细信息,请参见reagent问题解释

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接