Запись избыточного состояния навигации в состоянии модели? - PullRequest
2 голосов
/ 16 июня 2019

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

{concerts, venues}

Я также использовал реагирующую-навигацию-редукторы-помощники для размещения своей навигациисостояние в дереве:

{concerts, venues, nav}

Однако я хочу записать информацию о состоянии видимости конкретной модели.Когда отображается ConcertScreen, я хочу знать, когда пользователь смотрит / перестает смотреть конкретный идентификатор концерта (и сообщает серверу), с возможной целью измерить, как долго конкретный идентификатор концерта был виден на экране.

Я сделал это, добавив ветви для Navigation/NAVIGATE, Navigation/RESET и Navigation/BACK в редуктор концертов и установив visible: true для соответствующего объекта в концертах.

Это может привести к ошибкам, поскольку состояние навигации может быть изменено с помощью действий, отличных от этих конкретных действий.(Например, действие выхода из системы, выполняемое непосредственно редуктором Nav).

Я вижу две альтернативы, обе не идеальные:

  1. Используйте props.navigation.addListener для прослушивания фокусаи размытие событий на ConcertScreen, запуск пользовательских действий concertFocused / concertBlurred и обработка их в моем концертном редукторе вместо действий Navigation/*.

  2. Создание селектора, который вычисляетвидимый в данный момент концерт из состояния nav и рефакторинг бизнес-логики, которая ожидает, что concert.visible в качестве ввода будет использовать вместо этого селектор.

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

2 избегает дополнительных действий, но это кажется большим рефакторингом для не большого выигрыша, и это означает, что у меня естьчтобы вывести бизнес-логику из концертного редуктора и поместить ее в другое место.

Допустим, я использую опцию 2. Я добавляю промежуточное ПО, которое при любом действии применяет селектор к state.nav и изэто вычисляет то, что Концерт в настоящее время отображается.Если бы я хотел измерить продолжительность, как бы я сохранил время начала / окончания?Запустить новое действие с этими добавленными данными, чтобы концертный редуктор его уловил?Это просто похоже на вариант 1 с дополнительными шагами.

I может также иметь это промежуточное ПО, добавляющее поле к каждому действию, указывающее состояние отображения концерта, и позволяющее обработчику концертов обрабатывать его в случае по умолчанию / сквозного режима.Люди делают это?

1 Ответ

0 голосов
/ 23 июня 2019

Я бы подошел к вашему варианту использования таким образом, чтобы получить лучшее из обоих решений.

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

Позже, если я вас правильно понял, ваша цель - дать серверу знать статус видимости элемента (концерта) и, в конечном итоге, его видимое время. Если ваша цель - уведомить сервер только , не сообщая интерфейсным пользователям остальных приложений, почему вы хотите отслеживать его и в Redux? Вы можете пропустить часть Redux и отправлять обновления только на сервер.

Предположим, вам нужен Redux для отслеживания. Вы можете попытаться структурировать Store, как вы уже упоминали, добавив флаг visible к каждому объекту в хранилище Redux. Но если структура ваших элементов достаточно велика, и копирование и обновление объекта каждый раз при изменении флага visible обходится дорого, вы можете рассмотреть вопрос о создании выделенной ветви и редуктора магазина, которая будет отвечать только за потребности в отслеживании. Примерно так:

tracking : {
  concerts: {
    1: { visible: true, time: 10 }
  }
}

Теперь, обновляя флаг предмета, нужно скопировать и изменить только указанную выше крошечную структуру. Даже вы можете сделать его меньше и конкретнее для определенного типа предмета (trackingConcerts).

* Имейте в виду, вы сами решаете, будет ли такая специализированная ветвь Магазина улучшать производительность, потому что мы не знаем вашей подробной архитектуры и особенностей Магазина.

Продолжая с решениями ...

Как вы упомянули, использование навигационных действий + промежуточное программное обеспечение подвержено ошибкам. Как насчет варианта использования, у вас есть общая страница компонентов (то есть будет отправлено действие навигации с общим именем), но вы представите там один из ваших элементов (концерт)? Кроме того, рендеринг элемента всегда будет сопровождаться изменением логики сопоставления в вашем промежуточном программном обеспечении или там, где вы отслеживаете элементы по имени действия. Другой сложный случай - когда вы отображаете различные типы элементов (концерты, места проведения) на одной странице. Как вы будете различать и отслеживать элементы, учитывая, что у вас есть только 1 элемент навигации? Также в такой настройке я не вижу простого способа обработки видимого времени элемента.

О селекторах как о решении - они могут быть лишь малой частью решения. Селектор отвечает за выбор и управление производным состоянием. Ничего больше.

Покажите мне код, пожалуйста.

Я бы создал компонент-оболочку вокруг , реагировал бы на экран (или любую подобную библиотеку, которая отслеживает видимость компонента) и реализовывал только отслеживание видимого времени компонента.

Оболочка будет вызывать обратные вызовы при изменении состояния видимости компонента и обратный вызов на componentDidUnmount, включая видимое время.

Вот и все! Теперь вы можете прикреплять обработчики к этим обратным вызовам и обновлять Redux и / или уведомлять сервер об изменениях видимости, не полагаясь на какие-либо действия по навигации и промежуточное ПО.

Использование:

const App = () => (
  <Tracking
    onVisibilityChange={isVisible => {}}
    onUnmount={visibleSeconds => {}}
  >
    <Concert id={1} />
  </Tracking>
)

Оболочка отслеживания:

import TrackVisibility from 'react-on-screen'

const Tracking = ({ children, libraryProps, ...rest }) => (
  <TrackVisibility {...libraryProps}>
    <TrackingCore {...rest}>
      {children}
    </TrackingCore>
  </TrackVisibility>
)

TrackingCore, наша пользовательская логика отслеживания:

class TrackingCore extends React.Component {
  constructor (props) {
    super(props)

    this.state = {
      visibleSeconds: 0,
      interval: null
    }
  }

  componentDidMount() {
    this.track()
  }

  componentWillReceiveProps (nextProps) {
    this.track(nextProps)
  }

  componentDidUnmount() {
    const { visibleSeconds, interval } = this.state
    const { onUnmount } = this.props

    onUnmount(visibleSeconds)
    clearInterval(interval)
  }

  track (nextProps) {
    const { isVisible, onVisibilityChange } = this.props
    const { visibleSeconds, interval } = this.state

    const hasVisibilityChanged = (isVisible !== nextProps.isVisible) || !nextProps
    const isVisibleValue = nextProps ? nextProps.isVisible : isVisible

    // On visibility change, invoke the callback prop
    if (hasVisibilityChanged) {
      onVisibilityChange(isVisibleValue)

      // If it becomes visible, start counting the `visibleSeconds`
      if (isVisibleValue) {
        this.setState({
          interval: setInterval(() => this.setState({
            visibleSeconds: visibleSeconds + 1
          }), 1000)
        })
      } else {
        clearInterval(interval)
      }
    }
  }

  render () {
    return this.props.children
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...