2016-07-01 20 views
5

Normalizr eignet sich hervorragend zum Erstellen von strukturierten JSON-Repositorys von Entitäten.Redux normalizr + Umgang mit reduzierten Antworten

Wir haben viele Fälle, die Listen von Daten z.B. posts, die normalisiert wurden. Wenn posts aufgeführt sind, ist die API-Antwort auf einige Schlüsselfelder beschränkt.

Wir haben auch Fälle, in denen wir eine dieser posts anzeigen, obwohl wir nun die FULL JSON-Entität von der API mit allen Feldern abrufen müssen.

Wie ist es am besten, damit umzugehen?

Ein ein separates Reducer, Thunk/Saga, Selektoren und Aktionen?

B fügen Sie einfach die erweiterte Version des post ein, die von der API in den Reduzierer geholt wird. Die Selektoren usw. von früher wiederverwenden?

Antwort

5

Denken Sie an den Status der App als Datenbank. Ich schlage vor, Sie diesen Zustand Form zu verwenden:

{ 
    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', 
} 

Zunächst einmal haben wir unsere normalizr Schemas erstellen:

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

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

Nach Erfolg Antwort, wir Antwortdaten normalisieren und Versendung der Aktion:

// 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), 
    }); 
} 

In Reduzierungen müssen wir entities Reducer erstellen, der alle Aktionen abhören wird und wenn er entities Schlüssel in Zahlung hat Last, würde diese Einheiten in die App-Status hinzufügen:

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

Auch müssen wir Reduzierungen erstellen entsprechende FETCH_BOARDS und FETCH_FULL_BOARD Aktionen zu handhaben:

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

Ich habe eine Frage: 'fusionieren ({}, state, payload.entities);' den Zustand mutieren? – Daskus

+0

@Daskus Nein, da wir ein leeres Objekt als erstes Argument übergeben, wird die 'merge' Funktion ein neues Objekt zurückgeben. – 1ven

+0

Dies ist bei weitem die beste Antwort, wir gingen genau für diesen Ansatz. Der Schlüssel liegt darin, gute Selektoren und Filter zu schreiben. Empfehlen Sie auch unbedingt Immutable JS ...! – AndrewMcLagan

1

ich mit beiden Ihre beiden Entscheidungen zustimmen und würde zu demselben Schluss kommen. Aber lassen Sie uns sie genauer betrachten, um einen Vorteil gegenüber dem anderen zu sehen:

(B) Sie können die Post-Entitäten (Vorschau und vollständige Darstellung) als eine Einheit in Ihrem Reducer zusammenführen, aber Sie würden beibehalten Track der result Arrays (Vorschau und vollständige Darstellung), die Sie von den normalizrr normalisierten Daten nach den API-Anfragen erhalten würden. Dann können Sie später leicht unterscheiden, ob Sie bereits die vollständige Darstellung des Beitrags haben. Ihr Unterzustand könnte wie folgt aussehen:

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) Sie zwei Reduzierungen + Aktionen haben würde, eine für jede Darstellung, die Entitäten zu unterscheiden. Abhängig von der PREVIEW- oder FULL-Post-API-Anforderung würden Sie einen Ihrer Reduzierer über eine explizite Aktion bedienen. Ihre Unterzustände könnten wie diese aussehen:

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

Von einer sehr hohen Niveau Perspektive kann man schon sehen, dass Sie duplizierten Informationen speichern müßten.Die Post-Entität mit id: 2 würde zweimal mit ihrer Titeleigenschaft gespeichert werden: einmal für previewPostState und einmal für fullPostState. Wenn Sie die Titeleigenschaft in Ihrem globalen Status ändern möchten, müssten Sie dies an zwei Stellen tun. Man würde die einzige Quelle der Wahrheit in Redux verletzen. Das ist der Grund, warum ich mit der Wahl gehen würde (B): Sie haben einen Platz für Ihre Post-Entities, aber können deutlich ihre Darstellungen durch Ihre Ergebnis-Arrays unterscheiden.