web-dev-qa-db-fra.com

Redux: Opinions / exemples sur la façon de faire de la persistance backend?

Je me demande comment les gens utilisant Redux approchent leur persistance backend. En particulier, stockez-vous les "actions" dans une base de données ou stockez-vous uniquement le dernier état connu de l'application?

Si vous stockez les actions, les demandez-vous simplement au serveur, puis les relisez-elles toutes lorsqu'une page donnée se charge? Cela ne pourrait-il pas entraîner des problèmes de performances avec une application à grande échelle où il y a beaucoup d'actions?

Si vous ne stockez que "l'état actuel", comment persistez-vous réellement à un moment donné lorsque des actions se produisent sur un client?

Quelqu'un a-t-il des exemples de code sur la façon dont il connecte les réducteurs redux aux API de stockage backend?

Je sais que c'est une question très "cela dépend de votre application", mais je réfléchis juste à quelques idées ici et j'essaie de comprendre comment ce type d'architecture "sans état" pourrait fonctionner dans le sens d'une pile complète.

Merci tout le monde.

41
Lee

Persévérez définitivement l'état de vos réducteurs!

Si vous avez persisté à la place une séquence d'actions, vous ne pourrez jamais modifier vos actions dans votre frontend sans tripoter dans votre base de données de prod.

Exemple: conserver l'état d'un réducteur sur un serveur

Nous allons commencer avec trois types d'actions supplémentaires:

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

J'utilise redux-thunk pour faire des appels de serveur asynchrone: cela signifie qu'une fonction de créateur d'action peut dispatch des actions supplémentaires et inspecter l'état actuel.

Le créateur d'action save envoie immédiatement une action (afin que vous puissiez afficher une double flèche ou désactiver un bouton "enregistrer" dans votre interface utilisateur). Il envoie ensuite SAVE_SUCCESS ou un SAVE_ERROR actions une fois la requête POST terminée).

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 fonctionnerait totalement à la place de window.fetch trucs, je préfère juste ne pas charger tout jQuery pour une fonction!)

Le réducteur garde simplement une trace de toute demande de serveur en attente.

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
  }
}

Vous voudrez probablement faire quelque chose avec le someResponseValue qui est revenu du serveur - c'est peut-être un identifiant d'une entité nouvellement créée, etc.

J'espère que cela aide, cela a bien fonctionné jusqu'à présent pour moi!

33
Dan Fox

Persévérez définitivement les actions!

Ce n'est qu'un contre-exemple, ajoutant à Dan Fitch commentaire dans la réponse précédente.

Si vous persistez dans votre état, vous ne pourrez jamais modifier votre état sans modifier les colonnes et les tables de votre base de données. L'État vous montre seulement comment les choses sont maintenant, vous ne pouvez pas reconstruire un état précédent et vous ne saurez pas quels faits se sont produits.

Exemple: conserver une action sur un serveur

Votre action est déjà un "type" et une "charge utile", et c'est probablement tout ce dont vous avez besoin dans une architecture Event-Driven/Event-Sourcing.

Vous pouvez appeler votre back-end et envoyer les actions dans votre actionCreator (voir Dan Fox's answer ).

Une autre alternative consiste à utiliser un middleware pour filtrer les actions dont vous avez besoin pour persister, et les envoyer à votre backend, et, facultativement, envoyez de nouveaux événements à votre magasin.

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)
)

(Vous pouvez également utiliser un middleware pour envoyer l'état actuel au dossier sauvegardé. Appelez store.getState().)

Votre application sait déjà comment transformer des actions en état avec reducers , vous pouvez donc également récupérer des actions depuis votre backend.

0
Matruskan