2016-07-01 17 views
5

Normalizr è ideale per creare repository JSON strutturati di entità.Redux normalizr + che si occupa di risposte ridotte

In molti casi vengono visualizzati elenchi di dati, ad es. posts che sono stati normalizzati. Dove è elencato posts, la risposta dell'API è limitata a pochi campi chiave.

Abbiamo anche casi in cui viene visualizzato uno di questi posts anche se ora è necessario recuperare l'entità FULL JSON dall'API con tutti i campi.

Come è meglio affrontare questo?

A un riduttore separato, thunk/saga, selettori e azioni?

B inserire semplicemente la versione estesa dello post scaricata dall'API nel riduttore. Riusare i selettori ecc di prima?

risposta

5

Pensa allo stato dell'app come a un database. Vi suggerisco di usare questa forma di stato:

{ 
    entities: { 
    // List of normalized posts without any nesting. No matter whether they have all fields or not. 
    posts: { 
     '1': { 
     id: '1', 
     title: 'Post 1', 
     }, 
     '2': { 
     id: '2', 
     title: 'Post 2', 
     } 
    }, 
    }, 
    // Ids of posts, which need to displayed. 
    posts: ['1', '2'], 
    // Id of full post. 
    post: '2', 
} 

Prima di tutto, stiamo creando le nostre normalizr schemi:

// schemas.js 
import { Schema, arrayOf } from 'normalizr'; 

const POST = new Schema('post'); 
const POST_ARRAY = arrayOf(POST); 

dopo la risposta di successo, stiamo normalizzando i dati di risposta e dispacciamento l'azione:

// actions.js/sagas.js 
function handlePostsResponse(body) { 
    dispatch({ 
    type: 'FETCH_POSTS', 
    payload: normalize(body.result, POST_ARRAY), 
    }); 
} 

function handleFullPostResponse(body) { 
    dispatch({ 
    type: 'FETCH_FULL_POST', 
    payload: normalize(body.result, POST), 
    }); 
} 

Nei riduttori, è necessario creare il riduttore entities, che ascolterà tutte le azioni e se ha la chiave entities in pagamento carico, aggiungerebbe questa entità allo Stato app:

// reducers.js 
import merge from 'lodash/merge'; 

function entities(state = {}, action) { 
    const payload = action.payload; 

    if (payload && payload.entities) { 
    return merge({}, state, payload.entities); 
    } 

    return state; 
} 

Inoltre abbiamo bisogno di creare corrispondenti riduttori per gestire FETCH_BOARDS e FETCH_FULL_BOARD azioni:

// Posts reducer will be storing only posts ids. 
function posts(state = [], action) { 
    switch (action.type) { 
    case 'FETCH_POSTS': 
     // Post id is stored in `result` variable of normalizr output. 
     return [...state, action.payload.result]; 
    default: 
     return state; 
    } 
} 

// Post reducer will be storing current post id. 
// Further, you can replace `state` variable by object and store `isFetching` and other variables. 
function post(state = null, action) { 
    switch (action.type) { 
    case 'FETCH_FULL_POST': 
     return action.payload.id; 
    default: 
     return state; 
    } 
} 
+0

Ho una domanda: 'Unisci ({}, stato, payload.entities);' modifica lo stato? – Daskus

+0

@Daskus No, dato che stiamo passando l'oggetto vuoto come primo argomento, la funzione 'merge' restituirà un nuovo oggetto. – 1ven

+0

Questa è di gran lunga la migliore risposta, abbiamo finito per andare esattamente per questo approccio. la chiave sta nello scrivere buoni selettori e filtri. Raccomandiamo anche l'uso di Immutable JS ...! – AndrewMcLagan

1

Sono d'accordo con entrambe le due scelte e avrebbe vieni alla stessa conclusione. Ma diamo uno sguardo più da vicino a loro a vedere una forma vantaggio uno sopra l'altro:

(B) È possibile unire l'entità post (anteprima e la rappresentazione completa) come un'unica entità nel riduttore, ma si dovrebbe tenere traccia degli array result (anteprima e rappresentazione completa), che otterresti dai normali dati normalizzati dopo le richieste dell'API. Quindi puoi facilmente distinguere in seguito, se hai già la piena rappresentazione del post. Il tuo sub-statale potrebbe essere simile alla seguente:

const postState = { 
    // merged results from PREVIEW api 
    previews: [1, 2, 3], 

    // merged results from FULL api 
    full: [2], 

    // all merged entities 
    entities: { 
    1: { 
     title: 'foo1' 
    }, 
    2: { 
     title: 'foo2', 
     body: 'bar', 
    }, 
    3: { 
     title: 'foo3' 
    } 
    } 
}; 

(A) Avreste due riduttori + azioni, una per ogni rappresentazione, per distinguere le entità. A seconda della richiesta dell'API di PREVIEW o FULL, servirai uno dei tuoi riduttori tramite un'azione esplicita. I suoi sub-stato potrebbe apparire come questi:

const previewPostState = { 
    // merged results from PREVIEW api 
    result: [1, 2, 3], 

    // all preview entities 
    entities: { 
    1: { 
     title: 'foo1' 
    }, 
    2: { 
     title: 'foo2', 
    }, 
    3: { 
     title: 'foo3' 
    } 
    } 
}; 

const fullPostState = { 
    // merged results from FULL api 
    result: [2], 

    // all full entities 
    entities: { 
    2: { 
     title: 'foo2', 
     body: 'bar' 
    } 
    } 
}; 

Dal punto di vista di altissimo livello si può già vedere che si dovrebbe salvare informazioni duplicate.L'entità post con id: 2 verrebbe salvata due volte con la relativa proprietà title: una volta per previewPostState e una volta per fullPostState. Una volta che vuoi cambiare la proprietà del titolo nel tuo stato globale, dovresti farlo in due punti. Si violerebbe l'unica fonte di verità in Redux. Questa è la ragione per cui andrei con la scelta (B): hai un posto per le tue entità di post, ma puoi distinguere chiaramente le loro rappresentazioni dagli array di risultati.

Problemi correlati