Đọc trạng thái ban đầu của cửa hàng trong Redux Reducer


85

Trạng thái ban đầu trong ứng dụng Redux có thể được đặt theo hai cách:

Nếu bạn chuyển trạng thái ban đầu cho cửa hàng của mình, làm thế nào để bạn đọc trạng thái đó từ cửa hàng và đặt nó trở thành đối số đầu tiên trong bộ giảm của bạn?

Câu trả lời:


185

TL; DR

Không có combineReducers()hoặc mã thủ công tương tự, initialStateluôn thắng state = ...trong bộ giảm tốc vì giá trị stateđược truyền cho bộ giảm tốc initialStatekhông phải undefined , vì vậy cú pháp đối số ES6 không được áp dụng trong trường hợp này.

Với combineReducers()các hành vi có nhiều sắc thái hơn. Những bộ giảm có trạng thái được chỉ định initialStatesẽ nhận được điều đó state. Các bộ giảm khác sẽ nhận được undefined và do đó sẽ trở lại state = ...đối số mặc định mà chúng chỉ định.

Nói chung, initialStatechiến thắng trạng thái được chỉ định bởi bộ giảm tốc. Điều này cho phép các bộ giảm chỉ định dữ liệu ban đầu có ý nghĩa đối với chúng làm đối số mặc định, nhưng cũng cho phép tải dữ liệu hiện có (toàn bộ hoặc một phần) khi bạn cung cấp dịch vụ lưu trữ từ một số lưu trữ liên tục hoặc máy chủ.

Trước tiên, hãy xem xét một trường hợp mà bạn có một bộ giảm tốc duy nhất.
Nói rằng bạn không sử dụng combineReducers().

Sau đó, bộ giảm tốc của bạn có thể trông như thế này:

function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT': return state + 1;
  case 'DECREMENT': return state - 1;
  default: return state;
  }
}

Bây giờ giả sử bạn tạo một cửa hàng với nó.

import { createStore } from 'redux';
let store = createStore(counter);
console.log(store.getState()); // 0

Trạng thái ban đầu bằng không. Tại sao? Bởi vì đối số thứ hai createStoreundefined. Đây là statelần đầu tiên được chuyển cho bộ giảm tốc của bạn. Khi Redux khởi tạo, nó sẽ gửi một hành động "giả" để lấp đầy trạng thái. Vì vậy, bộ countergiảm tốc của bạn được gọi là statebằng undefined. Đây chính xác là trường hợp "kích hoạt" đối số mặc định. Do đó, statebây giờ 0là theo stategiá trị mặc định ( state = 0). Trạng thái này ( 0) sẽ được trả về.

Hãy xem xét một kịch bản khác:

import { createStore } from 'redux';
let store = createStore(counter, 42);
console.log(store.getState()); // 42

Tại sao lại là 42, và không phải 0, lần này? Bởi vì createStoređược gọi 42là đối số thứ hai. Đối số này trở thành đối số stateđược chuyển đến trình giảm bớt của bạn cùng với hành động giả. Lần này, statekhông phải là không xác định (đó là 42!), Vì vậy cú pháp đối số mặc định của ES6 không có tác dụng. Các state42, và 42được trả về từ giảm tốc.


Bây giờ chúng ta hãy xem xét một trường hợp mà bạn sử dụng combineReducers().
Bạn có hai bộ giảm tốc:

function a(state = 'lol', action) {
  return state;
}

function b(state = 'wat', action) {
  return state;
}

Bộ giảm tốc được tạo bởi combineReducers({ a, b })trông giống như sau:

// const combined = combineReducers({ a, b })
function combined(state = {}, action) {
  return {
    a: a(state.a, action),
    b: b(state.b, action)
  };
}

Nếu chúng ta gọi createStoremà không có initialState, nó sẽ khởi tạo stateđến {}. Do đó, state.astate.bsẽ undefinedtheo thời gian nó gọi abgiảm. Cả hai avà các bộ bgiảm sẽ nhận undefinedlàm đối số của chúng state và nếu chúng chỉ định statecác giá trị mặc định , các giá trị đó sẽ được trả về. Đây là cách trình giảm thiểu kết hợp trả về một { a: 'lol', b: 'wat' }đối tượng trạng thái trong lần gọi đầu tiên.

import { createStore } from 'redux';
let store = createStore(combined);
console.log(store.getState()); // { a: 'lol', b: 'wat' }

Hãy xem xét một kịch bản khác:

import { createStore } from 'redux';
let store = createStore(combined, { a: 'horse' });
console.log(store.getState()); // { a: 'horse', b: 'wat' }

Bây giờ tôi đã chỉ định initialStatelà đối số cho createStore(). Trạng thái được trả về từ bộ giảm tốc kết hợp kết hợp trạng thái ban đầu mà tôi đã chỉ định cho bộ agiảm tốc với 'wat'đối số mặc định được chỉ định mà bộ bgiảm tốc đã chọn chính nó.

Hãy nhớ lại những gì mà bộ giảm tốc kết hợp làm:

// const combined = combineReducers({ a, b })
function combined(state = {}, action) {
  return {
    a: a(state.a, action),
    b: b(state.b, action)
  };
}

Trong trường hợp này, stateđã được chỉ định để nó không rơi trở lại {}. Đó là một đối tượng có atrường bằng 'horse', nhưng không có btrường. Đây là lý do tại sao bộ agiảm thiểu nhận được 'horse'là của nó statevà vui mừng trả lại nó, nhưng bộ bgiảm thiểu nhận được undefinednhư của nó statevà do đó trả lại ý tưởng về mặc định state(trong ví dụ của chúng tôi, 'wat'). Đây là cách chúng tôi nhận { a: 'horse', b: 'wat' }lại.


Tóm lại, nếu bạn tuân theo các quy ước Redux và trả về trạng thái ban đầu từ các bộ giảm khi chúng được gọi undefinedstateđối số (cách dễ nhất để triển khai điều này là chỉ stateđịnh giá trị đối số mặc định của ES6), bạn sẽ có một hành vi hữu ích tốt cho các bộ giảm kết hợp. Họ sẽ thích giá trị tương ứng trong initialStateđối tượng mà bạn truyền cho createStore()hàm, nhưng nếu bạn không chuyển bất kỳ giá trị nào hoặc nếu trường tương ứng không được đặt, thì stateđối số mặc định được chỉ định bởi trình rút gọn sẽ được chọn thay thế.Cách tiếp cận này hoạt động tốt vì nó cung cấp cả khởi tạo và hydrat hóa dữ liệu hiện có, nhưng cho phép từng bộ giảm tốc đặt lại trạng thái của chúng nếu dữ liệu của chúng không được bảo toàn. Tất nhiên, bạn có thể áp dụng mô hình này một cách đệ quy, vì bạn có thể sử dụng combineReducers()ở nhiều cấp độ, hoặc thậm chí soạn các bộ giảm tốc theo cách thủ công bằng cách gọi các bộ giảm và cung cấp cho chúng phần có liên quan của cây trạng thái.


3
Cảm ơn vì chi tiết tuyệt vời - chính xác những gì tôi đang tìm kiếm. Tính linh hoạt của API của bạn ở đây là tuyệt vời. Phần tôi không nhìn thấy là bộ giảm thiểu "con" sẽ hiểu phần nào của trạng thái ban đầu thuộc về nó dựa trên khóa được sử dụng trong đó combineReducers. Cảm ơn bạn một lần nữa rất nhiều.
cantera

Cảm ơn vì câu trả lời này, Dan. Nếu tôi đang hiểu câu trả lời của bạn một cách chính xác, bạn đang nói rằng tốt nhất nên đặt trạng thái ban đầu thông qua loại hành động của bộ giảm tốc? Ví dụ: GET_INITIAL_STATE và gọi một công văn đến hành động đó nhập vào, giả sử gọi lại componentDidMount?
Con Antonakos

@ConAntonakos Không, ý tôi không phải thế. Ý tôi là chọn trạng thái ban đầu ngay nơi bạn xác định bộ giảm tốc, ví dụ function counter(state = 0, action)hoặc function visibleIds(state = [], action).
Dan Abramov

1
@DanAbramov: sử dụng đối số thứ hai trong createestore (), làm cách nào để tôi có thể bù nước cho trạng thái khi tải lại trang SPA với trạng thái được lưu trữ cuối cùng trong redux thay vì các giá trị nguyên. Tôi đoán tôi đang hỏi làm cách nào để có thể lưu vào bộ nhớ cache toàn bộ cửa hàng và bảo quản nó khi tải lại và chuyển nó vào chức năng tạo.
jasan

1
@jasan: Sử dụng localStorage. egghead.io/lessons/…
Dan Abramov,

5

Tóm lại: Redux là người chuyển trạng thái ban đầu cho các bộ giảm, bạn không cần phải làm gì cả.

Khi bạn gọi, createStore(reducer, [initialState])bạn đang cho Redux biết trạng thái ban đầu được chuyển tới trình giảm bớt khi hành động đầu tiên xảy ra.

Tùy chọn thứ hai mà bạn đề cập, chỉ áp dụng trong trường hợp bạn không vượt qua trạng thái ban đầu khi tạo cửa hàng. I E

function todoApp(state = initialState, action)

trạng thái sẽ chỉ được khởi tạo nếu không có trạng thái nào được Redux thông qua


1
Cảm ơn bạn đã phản hồi - trong trường hợp cấu thành bộ rút gọn, nếu bạn đã áp dụng trạng thái ban đầu cho cửa hàng, làm cách nào để bạn cho bộ giảm thiểu con / phụ biết phần nào của cây trạng thái ban đầu mà nó sở hữu? Ví dụ: nếu bạn có menuState, làm cách nào để yêu cầu trình giảm trình đơn đọc giá trị của state.menucửa hàng và sử dụng giá trị đó làm trạng thái ban đầu?
cantera,

Cảm ơn, nhưng câu hỏi của tôi phải không rõ ràng. Tôi sẽ chờ xem những người khác phản hồi trước khi chỉnh sửa.
cantera,

1

làm thế nào để bạn đọc trạng thái đó từ cửa hàng và biến nó thành đối số đầu tiên trong bộ giảm của bạn?

connectReducers () thực hiện công việc cho bạn. Cách đầu tiên để viết nó không thực sự hữu ích:

const rootReducer = combineReducers({ todos, users })

Nhưng cái khác, tương đương thì rõ ràng hơn:

function rootReducer(state, action) {
   todos: todos(state.todos, action),
   users: users(state.users, action)
}

0

Tôi hy vọng điều này trả lời yêu cầu của bạn (mà tôi hiểu là khởi tạo bộ giảm trong khi truyền intialState và trả về trạng thái đó)

Đây là cách chúng tôi thực hiện (cảnh báo: được sao chép từ mã Typecript).

Ý chính của nó là if(!state)kiểm tra trong hàm mainReducer (nhà máy)

function getInitialState(): MainState {

     return {
         prop1:                 'value1',
         prop1:                 'value2',
         ...        
     }
}



const reducer = combineReducers(
    {
        main:     mainReducer( getInitialState() ),
        ...
    }
)



const mainReducer = ( initialState: MainState ): Reducer => {

    return ( state: MainState, action: Action ): MainState => {

        if ( !state ) {
            return initialState
        }

        console.log( 'Main reducer action: ', action ) 

        switch ( action.type ) {
            ....
        }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.