2

我使用 Clojurescript re-frame 和试剂处理本机应用程序。我有一个文本输入组件,并且有两个版本的代码:

版本 1:输入文本是一个单独的组件,状态原子作为参数传递,与试剂库文档和示例中推荐的相同。

(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 没有任何问题,我可以尽可能快地打字。

根据试剂文档,将 r/atom 作为参数传递不应引起任何性能问题。

我在这里做错了吗?在这里避免性能损失的最佳方法是什么。

这是一个小例子,使用一个组件而不是两个组件并不是什么大问题,但是在良好的实践中将一个大组件拆分为多个较小的组件。这里的状态是组件本地的,我不想为它使用 re-frame。

4

3 回答 3

1

re-frame/dispatch将您的事件放入队列中以供重新处理以进行处理,因此在实际通过之前可能会稍有延迟,并且您的更改将在那里。

听起来你遇到了同样的问题:https ://github.com/day8/re-frame/issues/368

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

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

在上面的问题中提到的还有https://github.com/Day8/re-com据说可以以某种方式解决这个问题,但我没有仔细研究。

您的解决方案 #2 也没有错,它只是为您提供了一种不同的工作方式。因此,例如,如果您需要 app-db 中的数据在每次按键时更新,则只有类似于 #1 的内容才能工作。或者使用解决方案#2,但将原子作为参数传递给您的组件。

于 2020-04-10T07:23:20.667 回答
1

你的两个版本都有问题。使用本地状态时应该使用 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]])

于 2020-04-10T18:50:10.497 回答
0

看起来这里的问题是reagent由于其异步性质,它不支持具有受控输入的良好组件。

应避免受控输入(通过:value),或通过在更改后立即强制组件更新来解决:value

有关详细信息,请参阅试剂问题说明

于 2020-04-15T08:17:54.657 回答