2016-02-01 3 views
0

Imaginez que vous développiez une application de réaction-redux (avec un état d'arborescence global immuable). Et certaines données ont des règles-relations dans différentes branches d'arbre, comme les relations SQL entre les tables.Redux.js et les données avec les relations

I.e. Si vous travaillez sur la liste todos d'une entreprise, chaque todo a une relation (many-to-one) avec un utilisateur concret. Et si vous ajoutez un nouvel utilisateur, vous devez ajouter une liste de tâches vide (vers une autre branche dans l'état). Ou supprimer un utilisateur signifie que vous devez réattribuer les todos de l'utilisateur à un utilisateur (administrateur par défaut).

Vous pouvez coder cette relation directement dans le code source. Et c'est bon et ça marche bien. Mais imaginez que vous avez des millions de petites relations pour des données comme celle-ci. Il vaudra mieux que certaines petites opérations/vérifications «automatiques» (pour les relations de soutien/garde) se déroulent automatiquement selon les règles.

Peut être existait une approche commune/bibliothèque/expérience pour le faire via un ensemble de règles: comme déclencheurs dans SQL:

on add new user => add new empty todos

on user delete => reassign todos to default user

Répondre

1

Il y a deux solutions ici. Je ne pense pas que vous devriez viser à avoir ce genre de fonctionnalité dans une application redux, donc mon premier exemple n'est pas tout à fait ce que vous cherchez, mais je pense que c'est plus conique. Le deuxième exemple adopte un modèle DB/orm, qui peut fonctionner correctement, mais qui n'est pas conique, et nécessite

Ceux-ci peuvent être trivialement ajoutés en toute sécurité avec vanilla redux et redux-thunk. Redux thunk vous permet fondamentalement d'envoyer une seule action que son auto distribue plusieurs autres actions - donc lorsque vous déclenchez CREATE_USER, faites juste quelque chose le long des lignes de déclenchement CREATE_EMPTY_TODO, CREATE_USER, et ASSIGN_TODO dans l'action createUser. Pour supprimer des utilisateurs, REASSIGN_USER_TODOS puis DELETE_USER.

Pour les exemples que vous fournissez, voici des exemples:

function createTodoList(todos = []) { 
    return dispatch => { 
    return API.createTodoList(todos) 
     .then(res => { // res = { id: 15543, todos: [] } 
     dispatch({ type: 'CREATE_TODO_LIST_SUCCESS', res }); 
     return res; 
     }); 
    } 
} 

function createUser (userObj) { 
    return dispatch => { 
    dispatch(createTodoList()) 
     .then(todoListObj => { 
     API.createUser(Object.assign(userObj, { todoLists: [ todoListObj.id ] })) 
      .then(res => { // res = { id: 234234, name: userObj.name, todoLists: [ 15534 ]} 
      dispatch({ type: 'CREATE_USER_SUCCESS', payload: res }); 
      return res; 
      }) 
     }) 
     .catch(err => console.warn('Could not create user because there was an error creating todo list')); 

    } 
} 

deleteing, trucs sans async.

function deleteUser (userID) { 
    return (dispatch, getState) => { 
    dispatch({ 
     type: 'REASSIGN_USER_TODOS', 
     payload: { 
     fromUser: userID, 
     toUser: getState().application.defaultReassignUser 
     }); 

    dispatch({ 
     type: 'DELETE_USER', 
     payload: { userID } 
    }); 
    } 
} 

Le problème avec cette méthode, comme mentionné dans les commentaires, est qu'un nouveau développeur pourrait venir sur le projet sans savoir quelles actions existent déjà, et créer leur propre version de createUser qui ne sait pas créer des todos. Bien que vous ne puissiez jamais complètement enlever leur capacité à écrire du mauvais code, vous pouvez essayer d'être plus défensif en rendant vos actions plus structurées. Par exemple, si vos actions ressemblent à ceci:

const createUserAction = { 
    type: 'CREATE', 
    domain: 'USERS', 
    payload: userProperies 
} 

vous pouvez avoir un réducteur structuré comme celui-ci

function createUserTrigger (state, userProperies) { 
    return { 
    ...state, 
    todoLists: { 
     ...state.todoLists, 
     [userProperies.id]: [] 
    } 
    } 
} 

const triggers = { 
    [CREATE]: { 
    [USERS]: createUserTrigger 
    } 
} 

function rootReducer (state = initialState, action) { 
    const { type, domain, payload } = action; 
    let result = state; 

    switch (type) { 
    case CREATE: 
     result = { 
     ...state, 
     [domain]: { 
      ...state[domain], 
      [payload.id]: payload 
     } 
     }; 
     break; 
    case DELETE: 
     delete state[domain][payload.id]; 
     result = { ...state }; 
     break; 
    case UPDATE: 
     result = { 
     ...state, 
     [domain]: { 
      ...state[domain], 
      [payload.id]: _.merge(state[domain][payload.id], payload) 
     } 
     } 
     break; 
    default: 
     console.warn('invalid action type'); 
     return state; 
    } 

    return triggers[type][domain] ? triggers[type][domain](result, payload) : result; 
} 

Dans ce cas, vous forçant essentiellement tous les développeurs d'utiliser un possible très limité des types d'action. C'est très rigide et je ne le recommande pas vraiment, mais je pense que c'est ce que vous demandez.

+0

Merci pour votre réponse, Ai-je raison que vous suggériez de diviser la grande fonction de manutention à une petite, et appelez-le un par un. Je comprends et cela fonctionne bien. Mais je pose dans ma question quelque chose comme un déclencheur. C'est à dire. chaque fois qu'une nouvelle valeur sera ajoutée à un endroit quelconque de l'arbre (de l'état de l'application) (c'est-à-dire à la liste des utilisateurs), effectuez une opération spécifique comme "CREATE_EMPTY_TODO".il semble l'ajouter au "maître réducteur", c'est-à-dire l'exécuter après chaque changement d'arbre, mais cela pourrait être très coûteux (comme vérifier les relations de données pour une longue liste d'utilisateurs). – IL55

+0

Vous êtes donc préoccupé par les actions à vérifier contre tous les réducteurs pour leurs matchs? Par exemple, vous ne voulez pas que l'intégralité de 'rootReducer' regarde tous les gestionnaires pour ceux qui correspondent' CREATE_EMPTY_TODO'? En outre, l'idée d'un «déclencheur» en dehors de votre créateur d'action est identique à mon exemple - l'implémentation est effectivement la même, mais peut-être est-elle gérée dans le middleware. Il n'y a aucun moyen de mettre à jour l'état basé sur * comment * un réducteur a changé; Seules les actions ne peuvent pas initier un changement d'état. Toute autre source de changement d'état est un anti-pattern en redux. –

+0

Oui, je pensais à un langage comme SQL (et un certain DB de mémoire), pour la relation de données de soutien pour l'état de l'application. Imaginons que quelqu'un d'autre qui ne connaît pas la relation entre l'utilisateur et todos est en train d'implémenter un nouveau réducteur (comme importer des utilisateurs à partir d'un fichier). Et il va manipuler l'état de l'application "directement" (sans utiliser l'action "createUser"). Donc, il oublie d'ajouter des todos vides. Avec certains magasin de données-orienté-orienté, que les problèmes avec les données fixeront automatiquement (accordant des règles de relations) .... – IL55