简体   繁体   English

在Redux Reducer中读取Store的初始状态

[英]Read Store's Initial State in Redux Reducer

Initial state in a Redux app can be set in two ways: Redux应用程序中的初始状态可以通过两种方式设置:

  • pass it as the second argument to createStore ( docs link ) 将它作为createStore的第二个参数传递( docs链接
  • pass it as the first argument to your (sub-)reducers ( docs link ) 将它作为第一个参数传递给(子)减速器( docs link

If you pass initial state to your store, how do you read that state from the store and make it the first argument in your reducers? 如果您将初始状态传递到商店,那么如何从商店中读取该状态并将其作为减速器中的第一个参数?

TL;DR TL; DR

Without combineReducers() or similar manual code, initialState always wins over state = ... in the reducer because the state passed to the reducer is initialState and is not undefined , so the ES6 argument syntax doesn't get applied in this case. 如果没有combineReducers()或类似的手动代码, initialState总是战胜state = ...在减速,因为state传递到减速机 initialState而不是 undefined ,所以ES6参数语法不会在这种情况下得到应用。

With combineReducers() the behavior is more nuanced. 使用combineReducers() ,行为更加微妙。 Those reducers whose state is specified in initialState will receive that state . 那些在initialState指定状态的reducer将接收该state Other reducers will receive undefined and because of that will fall back to the state = ... default argument they specify. 其他reducers将收到undefined ,因此会回退到他们指定的state = ... default参数。

In general, initialState wins over the state specified by the reducer. 通常, initialState胜过reducer指定的状态。 This lets reducers specify initial data that makes sense to them as default arguments, but also allows loading existing data (fully or partially) when you're hydrating the store from some persistent storage or the server. 这使得Reducer可以指定对它们有意义的初始数据作为默认参数,但是当您从某个持久存储或服务器中保存商店时,还允许加载现有数据(完全或部分)。

First let's consider a case where you have a single reducer. 首先让我们考虑一下你有一个减速器的情况。
Say you don't use combineReducers() . 假设您不使用combineReducers()

Then your reducer might look like this: 然后你的reducer可能看起来像这样:

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

Now let's say you create a store with it. 现在让我们说你用它创建一个商店。

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

The initial state is zero. 初始状态为零。 Why? 为什么? Because the second argument to createStore was undefined . 因为createStore的第二个参数undefined This is the state passed to your reducer the first time. 这是state传递给你减速的第一次。 When Redux initializes it dispatches a “dummy” action to fill the state. 当Redux初始化时,它会调度“虚拟”动作来填充状态。 So your counter reducer was called with state equal to undefined . 所以你的counter减速器被调用state等于undefined This is exactly the case that “activates” the default argument. 这正是“激活”默认参数的情况。 Therefore, state is now 0 as per the default state value ( state = 0 ). 因此, state是现在0按默认state值( state = 0 )。 This state ( 0 ) will be returned. 将返回此状态( 0 )。

Let's consider a different scenario: 让我们考虑一个不同的场景:

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

Why is it 42 , and not 0 , this time? 为什么这次是42而不是0 Because createStore was called with 42 as the second argument. 因为使用42作为第二个参数调用createStore This argument becomes the state passed to your reducer along with the dummy action. 此参数将成为与reduction一起传递给reducer的state This time, state is not undefined (it's 42 !), so ES6 default argument syntax has no effect. 这次, state未定义(它是42 !),因此ES6默认参数语法无效。 The state is 42 , and 42 is returned from the reducer. state42 ,从减速器返回42


Now let's consider a case where you use combineReducers() . 现在让我们考虑使用combineReducers()
You have two reducers: 你有两个减速器:

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

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

The reducer generated by combineReducers({ a, b }) looks like this: combineReducers({ a, b })生成的combineReducers({ a, b })如下所示:

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

If we call createStore without the initialState , it's going to initialize the state to {} . 如果我们在没有initialState情况下调用createStore ,它将把state初始化为{} Therefore, state.a and state.b will be undefined by the time it calls a and b reducers. 因此, state.astate.b将在调用ab reducers时被undefined Both a and b reducers will receive undefined as their state arguments, and if they specify default state values, those will be returned. ab reducers都将接收undefined作为 state参数,如果它们指定默认state值,则将返回这些参数。 This is how the combined reducer returns a { a: 'lol', b: 'wat' } state object on the first invocation. 这就是组合的reducer在第一次调用时返回{ a: 'lol', b: 'wat' }状态对象的方式。

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

Let's consider a different scenario: 让我们考虑一个不同的场景:

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

Now I specified the initialState as the argument to createStore() . 现在我将initialState指定为createStore()的参数。 The state returned from the combined reducer combines the initial state I specified for the a reducer with the 'wat' default argument specified that b reducer chose itself. 状态返回从组合减速器结合我为指定的初始状态a与减速器'wat'默认参数指定b减速器选择本身。

Let's recall what the combined reducer does: 让我们回想一下联合减速机的作用:

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

In this case, state was specified so it didn't fall back to {} . 在这种情况下,指定了state ,因此它不会回退到{} It was an object with a field equal to 'horse' , but without the b field. 它是一个对象, a字段等于'horse' ,但没有b字段。 This is why the a reducer received 'horse' as its state and gladly returned it, but the b reducer received undefined as its state and thus returned its idea of the default state (in our example, 'wat' ). 这就是为什么a减速收到'horse'作为其state ,并愉快地回到住处,但b收到减速undefinedstate ,从而恢复默认的思想 state (在本例中, 'wat' )。 This is how we get { a: 'horse', b: 'wat' } in return. 这就是我们获得{ a: 'horse', b: 'wat' }作为回报的方式。


To sum this up, if you stick to Redux conventions and return the initial state from reducers when they're called with undefined as the state argument (the easiest way to implement this is to specify the state ES6 default argument value), you're going to have a nice useful behavior for combined reducers. 总而言之,如果你坚持使用Redux约定并在使用undefined调用undefined作为state参数时返回初始状态(实现它的最简单方法是指定state ES6默认参数值),你就是对组合减速器有一个很好的有用行为。 They will prefer the corresponding value in the initialState object you pass to the createStore() function, but if you didn't pass any, or if the corresponding field is not set, the default state argument specified by the reducer is chosen instead. 他们更喜欢传递给createStore()函数的initialState对象中的相应值,但如果没有传递任何值,或者未设置相应的字段,则选择reducer指定的默认state参数。 This approach works well because it provides both initialization and hydration of existing data, but lets individual reducers reset their state if their data was not preserved. 这种方法很有效,因为它提供了现有数据的初始化和水合作用,但如果不保留数据,则允许各个reducers重置其状态。 Of course you can apply this pattern recursively, as you can use combineReducers() on many levels, or even compose reducers manually by calling reducers and giving them the relevant part of the state tree. 当然,您可以递归地应用此模式,因为您可以在许多级别上使用combineReducers() ,甚至可以通过调用reducers并将它们赋予状态树的相关部分来手动组合combineReducers()

In a nutshell: it's Redux the one who passes the initial state to the reducers, you don't need to do anything. 简而言之:Redux是将初始状态传递给reducer的人,你不需要做任何事情。

When you call createStore(reducer, [initialState]) you are letting Redux know what is the initial state to be passed to the reducer when the first action comes in. 当你调用createStore(reducer, [initialState])你会让Redux知道在第一个动作进入时传递给reducer的初始状态是什么。

The second option you mention, applies only in case you didn't pass an initial state when creating the store. 您提到的第二个选项仅适用于您在创建商店时未通过初始状态的情况。 ie

function todoApp(state = initialState, action)

state will only be initialised if there was no state passed by Redux 只有在Redux没有通过状态时才会初始化状态

how do you read that state from the store and make it the first argument in your reducers? 你如何从商店中读取该状态并将其作为减速器中的第一个参数?

combineReducers() do the job for you. combineReducers()为您完成工作。 The first way to write it is not really helpfull : 写它的第一种方式并不是真的有用:

const rootReducer = combineReducers({ todos, users })

But the other one, that is equivalent is more clear : 但另一个,相当于更清楚:

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

I hope this answers your request (which I understood as initializing reducers while passing intialState and returning that state) 我希望这能回答你的请求(我理解为在传递intialState并返回该状态时初始化reducers)

This is how we do it (warning: copied from Typescript code). 这就是我们这样做的方法(警告:从Typescript代码中复制)。

The gist of it is the if(!state) test in the mainReducer(factory) function 它的要点是mainReducer(工厂)函数中的if(!state)测试

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 ) {
            ....
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM