Проблема производительности реагента при передаче атома в качестве параметра функции - PullRequest
2 голосов
/ 09 апреля 2020

Я работаю над собственным приложением реакции, используя перекадровку Clojurescript и реагент. У меня есть один компонент ввода текста, и у меня есть две версии кода:

Версия 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.

Ответы [ 3 ]

1 голос
/ 10 апреля 2020

Обе ваши версии имеют проблемы. Вы должны использовать компоненты типа 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]])))

Более подробную информацию о Форме-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]])

1 голос
/ 10 апреля 2020

re-frame/dispatch помещает ваши события в очередь для обработки повторного кадра, так что может пройти небольшая задержка, прежде чем он действительно пройдет, и ваши изменения будут там.

Похоже, вы испытываете та же проблема, заявленная здесь: https://github.com/day8/re-frame/issues/368

Таким образом, одним из обходных путей является использование re-frame.core/dispatch-sync, которое заставляет перекадр обрабатывать событие напрямую и синхронно. Возможно, вам также придется добавить вызов к reagent.core/flush, чтобы принудительно повторно выполнить рендеринг компонента. Мне раньше не требовался грипп sh при создании веб-клиентов, но React Native, кажется, работает по-другому.

Подробнее об этих двух функциях можно прочитать здесь:

Упомянутый выше вопрос также https://github.com/Day8/re-com, который предположительно как-то решает проблему, но я не стал на это более пристально смотреть.

Ваше решение № 2 также не является неправильным, оно просто дает вам другой способ работы. Так что, если вам нужно, чтобы данные в вашей app-db обновлялись, например, при каждом нажатии клавиши, будет работать только что-то более похожее на # 1. Или используя решение № 2, но передав атом в качестве аргумента вашему компоненту.

0 голосов
/ 15 апреля 2020

Похоже, проблема здесь в том, что reagent не поддерживает скважинные компоненты с управляемыми входами из-за его асинхронной c природы.

Управляемый вход (через :value) следует избегать, или обойти, принудительно обновив компонент сразу после изменения :value.

Подробнее см. вопрос с реагентом и .

...