Redux: мнения/примеры того, как сделать постоянство бэкэнда?

Мне интересно, как люди, использующие Redux, приближаются к сохранению своего бэкэнда. В частности, вы храните «действия» в базе данных или сохраняете только последнее известное состояние приложения?

Если вы сохраняете действия, вы просто запрашиваете их с сервера, а затем воспроизводите их все при загрузке данной страницы? Не может ли это привести к некоторым проблемам с производительностью в крупномасштабном приложении с большим количеством действий?

Если вы сохраняете только «текущее состояние», как вы на самом деле сохраняете это состояние в любой момент времени, когда действия происходят на клиенте?

Есть ли у кого-нибудь примеры кода того, как они подключают редукторы редукса к api внутреннего хранилища?

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

Спасибо всем.


person Lee    schedule 05.10.2015    source источник
comment
Я думаю, что серверная часть обычно довольно классическая (обычная БД), не отличающаяся от других приложений CRUD. Однако вас могут заинтересовать такие подходы, как rethinkdb.com и confluent.io/blog/.   -  person Dan Abramov    schedule 09.10.2015


Ответы (2)


Определенно сохраните состояние ваших редукторов!

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

Пример: сохранение состояния одного редуктора на сервере

Мы начнем с трех дополнительных типов действий:

// actions: 'SAVE', 'SAVE_SUCCESS', 'SAVE_ERROR'

Я использую redux-thunk для выполнения асинхронных вызовов сервера: это означает, что одна функция создания действия может dispatch дополнительных действий и проверять текущий штат.

Создатель действия save немедленно отправляет одно действие (чтобы вы могли показать счетчик или отключить кнопку «Сохранить» в своем пользовательском интерфейсе). Затем он отправляет действия SAVE_SUCCESS или SAVE_ERROR после завершения запроса POST.

var actionCreators = {
  save: () => {
    return (dispatch, getState) => {
      var currentState = getState();
      var interestingBits = extractInterestingBitsFromState(currentState);

      dispatch({type: 'SAVE'});

      window.fetch(someUrl, {
        method: 'POST',
        body: JSON.stringify(interestingBits)
      })
      .then(checkStatus) // from https://github.com/github/fetch#handling-http-error-statuses
      .then((response) => response.json())
      .then((json) => dispatch actionCreators.saveSuccess(json.someResponseValue))
      .catch((error) =>
        console.error(error)
        dispatch actionCreators.saveError(error)
      );
    }
  },

  saveSuccess: (someResponseValue) => return {type: 'SAVE_SUCCESS', someResponseValue},

  saveError: (error) => return {type: 'SAVE_ERROR', error},

  // other real actions here
};

(N.B. $.ajax вполне подойдет вместо window.fetch, я просто предпочитаю не загружать весь jQuery для одной функции!)

Редьюсер просто отслеживает любой невыполненный запрос к серверу.

function reducer(state, action) {
  switch (action.type) {
    case 'SAVE':
      return Object.assign {}, state, {savePending: true, saveSucceeded: null, saveError: null}
      break;
    case 'SAVE_SUCCESS':
      return Object.assign {}, state, {savePending: false, saveSucceeded: true, saveError: false}
      break;
    case 'SAVE_ERROR': 
      return Object.assign {}, state, {savePending: false, saveSucceeded: false, saveError: true}
      break;

    // real actions handled here
  }
}

Вы, вероятно, захотите что-то сделать с someResponseValue, который вернулся с сервера — может быть, это идентификатор вновь созданного объекта и т. д. и т. д.

Я надеюсь, что это поможет, до сих пор это работало хорошо для меня!

person Dan Fox    schedule 10.10.2015
comment
Спасибо за вклад Дэн! Кажется, это работает хорошо! - person Lee; 11.10.2015
comment
Это большая помощь. Дополнительное примечание: если вы храните как сокращенное состояние, так и действия, вы можете использовать методы проектирования, управляемые событиями, чтобы разрешить перемещение во времени или управление версиями на стороне сервера. Полезно для некоторых вещей! - person Dan Fitch; 16.04.2017

Определенно продолжать действия!

Это всего лишь контрпример, добавленный к комментарий к предыдущему ответу.

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

Пример: сохранить действие на сервере

Ваш action уже является "типом" и "полезной нагрузкой", и это, вероятно, все, что вам нужно в архитектуре, управляемой событиями/источниками событий.

Вы можете вызвать серверную часть и отправить действия внутри своего actionCreator (см. ответ Дэна Фокса).

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

const persistenceActionTypes = ['CREATE_ORDER', 'UPDATE_PROFILE'];
// notPersistenceActionTypes = ['ADD_ITEM_TO_CART', 'REMOVE_ITEM_FROM_CART', 'NAVIGATE']

const persistenceMiddleware = store => dispatch => action => {
  const result = dispatch(action);
  if (persistenceActionTypes.indexOf(action.type) > -1) {
  // or maybe you could filter by the payload. Ex:
  // if (action.timestamp) {
      sendToBackend(store, action);
  }
  return result;
}

const sendToBackend = (store, action) => {
  const interestingBits = extractInterestingBitsFromAction(action);
  // déjà vu
  window.fetch(someUrl, {
    method: 'POST',
    body: JSON.stringify(interestingBits)
  })
  .then(checkStatus)
  .then(response => response.json())
  .then(json => {
    store.dispatch(actionCreators.saveSuccess(json.someResponseValue));
  })
  .catch(error => {
    console.error(error)
    store.dispatch(actionCreators.saveError(error))
  });
}
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk';

createStore(
  yourReducer,
  aPreloadedState,
  applyMiddleware(thunk, persistenceMiddleware)
)

(Вы также можете использовать промежуточное программное обеспечение для отправки текущего состояния на резервную копию. Вызовите store.getState().)

Ваше приложение уже умеет преобразовывать действия в состояние с помощью reducers, поэтому вы также можете получать действия из ваш бэкэнд тоже.

person Matruskan    schedule 16.07.2019