Ситуация
У меня есть сценарий адаптации, в котором пользователь проходит пошаговую адаптацию. Я хочу управлять состоянием клиента на стороне пользователя с помощью 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 приходит с новой информацией. Я не знаю о таком обратном вызове или другом решении этой ситуации.
Мои вопросы
В связи с этим у меня такие вопросы:
- Что я могу улучшить в существующих подходах? Есть ли что-то, что я сделал, что очень опасно / приводит к несоответствиям? (см. Мой прогресс)
- Как я могу синхронизировать хранилище Redux, когда по какой-то причине обновляется хранилище Relay. Я ищу метод жизненного цикла компонента React или обратный вызов Relay, где я могу затем отправить действие Redux в хранилище Redux. (см. Открытые проблемы)