Как синхронизировать Redux и Relay?

Ситуация

У меня есть сценарий адаптации, в котором пользователь проходит пошаговую адаптацию. Я хочу управлять состоянием клиента на стороне пользователя с помощью Redux. Синхронизация между сервером и клиентом уже реализована в Relay, но мне все еще нужен Redux store для управления состоянием на стороне клиента. Таким образом, возникают проблемы с синхронизацией Relay-/Redux-Store.

Что я делаю прямо сейчас, так это оборачиваю свой компонент React в Redux, а затем в Relay:

// OnboardProgressView.js
// ...      

// wrap React component with Redux 
const mapStateToProps = (state) => {
  return {
    onboardProgress: state.onboardProgress,
  }
}

const ReduxContainer = connect(
  mapStateToProps,
)(OnboardProgressView)

// this is only for convenience of access of the Relay data
const MappedOnboardProgressView = mapProps({
  params: (props) => props.params,
  user: (props) => props.viewer.user,
})(ReduxContainer)

// wrap Redux component with Relay
export default Relay.createContainer(MappedGettingStartedView, {
  fragments: {
    viewer: () => Relay.QL`
      fragment on Viewer {
        user {
          userId
          onboardProgressStep
        }
        # more stuff ...
      }
    `,
  },
})  

Мой прогресс

Я нашел способы выполнения различных операций следующим образом:

Инициализация хранилища Redux с данными сервера

Я инициализирую состояние Redux сразу после создания хранилища с помощью асинхронного необработанного запроса Relay. Чтобы сделать это возможным, я также использую промежуточное ПО redux-thunk. Redux инициирует запрос к Relay, который запрашивает сервер. Визуальное представление (стрелка обозначает поток данных, порядок элементов отражает «порядок вызова»): Redux <= Relay <= Server

// app.js
const store = createStore(reducer, applyMiddleware(thunk))
store.dispatch(fetchOnboardProgress())

// onboardProgress.js
export function fetchOnboardProgress () {
  return function (dispatch) {
    var query = Relay.createQuery(Relay.QL`
      query {
        viewer {
          user {
            id
            onboardProgress
          }
        }
      }`, {})

    return new Promise(function (resolve, reject) {
      Relay.Store.primeCache({query}, ({done, error}) => {
        if (done) {
          const data = Relay.Store.readQuery(query)[0]
          dispatch(update(data.user.onboardProgress, data.user.id))
          resolve()
        } else if (error) {
          reject(Error('Error when fetching onboardProgress'))
        }
      })
    })
  }
}

Обновление данных на сервере при отправке действия Redux

Redux => Relay => Server

Чтобы иметь согласованные изменения состояния, когда пользователь проходит процесс адаптации, я запускаю действие Redux, которое также асинхронно выполняет мутацию Relay. Я также использую redux-thunk для этой цели.

function nextStep () {
  return function (dispatch, getState) {
    const currentStep = getState().onboardProgress.step
    const currentStepIndex = OnboardProgress.steps.indexOf(currentStep)
    const nextStep = OnboardProgress.steps[currentStepIndex + 1]
    const userId = getState().onboardProgress._userId

    return _updateReduxAndRelay(dispatch, nextStep, userId)
  }
}

function _updateReduxAndRelay (dispatch, step, userId) {
  return new Promise((resolve, reject) => {
    Relay.Store.commitUpdate(new UpdateUserMutation({
      userId: userId,
      onboardProgressStep: step,
    }), {
      onSuccess: () => {
        dispatch(update(step, userId))
        resolve()
      },
      onFailure: reject,
    })
  })
}

export function update (step, userId) {
  const payload = {onboardProgress: new OnboardProgress({step, userId})}
  return {type: UPDATE, payload}
}

Открытые проблемы

Я так и не нашел выхода к следующей ситуации:

Обновление Redux Store при обновлении Relay Store

Изменения данных на сервере могут иметь внешние источники, которые не вызываются действием пользователя в нашем приложении. С Relay мы можем решить эту проблему с помощью forceFetching или опроса. Запрос реле выглядит следующим образом: Relay <= Server. Я хотел бы дополнительно иметь этот поток данных: Relay => Redux при изменении внешних данных.

Другая возможная причина необходимости обновления хранилища Redux новыми данными — это когда мы хотим синхронизировать данные, глубоко вложенные в хранилище Relay, или часть сложного запроса.

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

Если мы управляем этой информацией в Redux, нам нужен способ инициировать действие Redux, когда запрос Relay приходит с новой информацией. Я не знаю о таком обратном вызове или другом решении этой ситуации.

Мои вопросы

В связи с этим у меня такие вопросы:

  1. Что я могу улучшить в существующих подходах? Есть ли что-то, что я сделал, что очень опасно / приводит к несоответствиям? (см. Мой прогресс)
  2. Как я могу синхронизировать хранилище Redux, когда по какой-то причине обновляется хранилище Relay. Я ищу метод жизненного цикла компонента React или обратный вызов Relay, где я могу затем отправить действие Redux в хранилище Redux. (см. Открытые проблемы)

person marktani    schedule 18.07.2016    source источник
comment
Вы нашли лучшее решение? Я работаю над аналогичной проблемой.   -  person Anil Sharma    schedule 27.10.2016
comment
Проверьте это решение. Это похоже на ваше решение: gist.github.com/fdecampredon/f337605e393a5b032b85   -  person Anil Sharma    schedule 27.10.2016


Ответы (1)


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

person Igorsvee    schedule 19.07.2016
comment
Это звучит как возможное решение. Можете ли вы дать немного больше информации? Я думаю, что мог бы использовать сетевой уровень по умолчанию и дополнительно отправлять действия Redux в хранилище Redux в sendQueries/sendMutation. - person marktani; 20.07.2016