2016-07-01 19 views
5

Normalizr là tuyệt vời trong việc tạo các kho lưu trữ JSON có cấu trúc của các thực thể.Redux normalizr + đối phó với các phản hồi bị giảm

Chúng tôi có nhiều trường hợp hiển thị danh sách dữ liệu, ví dụ: posts đã được chuẩn hóa. Trong trường hợp posts được liệt kê, phản hồi API được giới hạn ở một vài trường khóa.

Chúng tôi cũng có trường hợp chúng tôi hiển thị một trong số posts mặc dù hiện tại chúng tôi cần tìm nạp thực thể JSON đầy đủ từ API với tất cả các trường.

Cách tốt nhất để giải quyết vấn đề này là gì?

A một bộ giảm tốc, bộ tách/saga, bộ chọn và hành động riêng biệt?

B chỉ cần chèn phiên bản mở rộng của post được tìm nạp từ API vào bộ giảm tốc. Sử dụng lại các bộ chọn vv từ trước?

Trả lời

5

Hãy nghĩ về trạng thái của ứng dụng dưới dạng cơ sở dữ liệu. Tôi đề nghị bạn sử dụng hình dạng trạng thái này:

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

Trước hết, chúng ta đang tạo normalizr schemas của chúng tôi:

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

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

Sau khi phản ứng thành công, chúng tôi được bình thường hóa dữ liệu phản ứng và cử hành động:

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

trong gia giảm, chúng ta cần tạo entities giảm, mà sẽ được nghe tất cả các hành động và nếu nó có entities quan trọng trong việc trả tải, sẽ thêm các đối tượng này sang trạng thái ứng dụng:

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

Ngoài ra chúng tôi cần phải tạo ra tương ứng gia giảm để xử lý FETCH_BOARDSFETCH_FULL_BOARD hành động:

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

Tôi có một câu hỏi: Liệu 'hợp nhất ({}, state, payload.entities);' biến đổi trạng thái? – Daskus

+0

@Daskus Không, khi chúng ta truyền đối tượng rỗng làm đối số đầu tiên, hàm 'merge' sẽ trả về đối tượng mới. – 1ven

+0

Đây là câu trả lời hay nhất, chúng tôi đã kết thúc chính xác cho phương pháp này. chìa khóa là viết các bộ chọn và bộ lọc tốt. Cũng rất khuyên bạn nên sử dụng JS bất biến ...! – AndrewMcLagan

1

Tôi đồng ý với cả hai sự lựa chọn của bạn và sẽ có đi đến cùng một kết luận. Nhưng chúng ta hãy xem xét kỹ hơn chúng để thấy một dạng lợi thế so với cái kia:

(B) Bạn có thể hợp nhất các thực thể bài (xem trước và biểu diễn đầy đủ) như một thực thể trong bộ giảm tốc, nhưng bạn sẽ giữ theo dõi các mảng result (xem trước và trình bày đầy đủ), mà bạn sẽ nhận được từ dữ liệu chuẩn hóa bình thường sau các yêu cầu API. Sau đó, bạn có thể dễ dàng phân biệt sau đó, nếu bạn đã có đại diện đầy đủ của bài đăng. tiểu bang của bạn có thể trông giống như sau:

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) Bạn sẽ có hai gia giảm + hành động, một cho mỗi đại diện, để phân biệt các đối tượng. Tùy thuộc vào yêu cầu API PREVIEW hoặc FULL posts, bạn sẽ phục vụ một trong các trình giảm tốc của mình thông qua một hành động rõ ràng. Tiểu bang của bạn có thể trông giống như sau:

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

Từ góc độ rất cao, bạn có thể thấy rằng bạn sẽ phải lưu thông tin trùng lặp.Thực thể bài đăng với id: 2 sẽ được lưu hai lần với thuộc tính tiêu đề: một lần cho previewPostState và một lần cho fullPostState. Khi bạn muốn thay đổi thuộc tính tiêu đề trong trạng thái toàn cầu của mình, bạn sẽ phải thực hiện tại hai vị trí. Người ta sẽ vi phạm nguồn gốc duy nhất của sự thật trong Redux. Đó là lý do tôi sẽ đi với sự lựa chọn (B): Bạn có một nơi cho các thực thể bài viết của mình, nhưng có thể phân biệt rõ ràng các biểu diễn của chúng bằng các mảng kết quả của bạn.

Các vấn đề liên quan