2016-07-01 4 views
5

Normalizr отлично справляется с созданием структурированных репозиториев JSON объектов.Redux normalizr + работа с уменьшенными ответами

У нас есть много случаев отображения списков данных, например. posts, которые были нормализованы. Где указано posts, ответ API ограничен несколькими ключевыми полями.

У нас также есть случаи, когда мы показываем один из этих posts, хотя теперь нам нужно получить FULL JSON-объект из API со всеми полями.

Как лучше справиться с этим?

A отдельный редуктор, thunk/saga, селектор и действия?

B просто вставьте расширенную версию post, извлеченную из API, в редуктор. Повторное использование селекторов и т. Д. Раньше?

ответ

5

Подумайте о состоянии приложения как базы данных. Я предлагаю вам использовать эту государственную форму:

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

Прежде всего, мы создаем наши normalizr схемы:

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

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

После ответа успеха, мы нормализации данных ответа и диспетчеризации действия:

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

В редукторах нам необходимо создать редуктор entities, который будет прослушивать все действия, и если у него есть entities ключ на оплату нагрузка будет добавить эти объекты в состояние приложения:

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

Кроме того, мы должны создать соответствующие редукторы для обработки FETCH_BOARDS и FETCH_FULL_BOARD действия:

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

У меня вопрос: «Слияние ({}, state, payload.entities);« мутировать состояние? – Daskus

+0

@Daskus Нет, поскольку мы передаем пустой объект в качестве первого аргумента, функция 'merge' вернет новый объект. – 1ven

+0

Это, безусловно, лучший ответ, мы в конечном итоге пошли именно на этот подход. ключ заключается в написании хороших селекторов и фильтров. Также настоятельно рекомендуем использовать Immutable JS ...! – AndrewMcLagan

1

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

(B) Вы можете объединить почтовые объекты (предварительный просмотр и полное представление) как единое целое в вашем редукторе, но вы бы держать отслеживание массивов result (предварительный просмотр и полное представление), которые вы получили бы от нормализованных данных после запросов API. Затем вы можете легко отличить потом, если у вас уже есть полное представление о должности. Ваше подсостояние может выглядеть следующим образом:

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) Вы бы два переходников + действия, по одному для каждого представления, чтобы различать объекты. В зависимости от запроса API PREVIEW или FULL, вы можете использовать один из ваших редукторов через одно явное действие. Ваши суб-государства могут выглядеть эти:

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

С точки зрения очень высокого уровня, вы уже можете видеть, что вы должны сохранить дублированный информацию.Почтовый объект с id: 2 будет сохранен два раза с его имущественным титулом: один раз за previewPostState и один раз за fullPostState. Как только вы захотите изменить свойство title в своем глобальном состоянии, вам нужно будет сделать это в двух местах. В Редуксе можно было бы нарушить единственный источник правды. Вот почему я поеду с выбором (B): У вас есть одно место для ваших почтовых объектов, но вы можете четко различать их представления по вашим массивам результатов.