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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33749759/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 15:25:31  来源:igfitidea点击:

Read Store's Initial State in Redux Reducer

javascriptreactjsredux

提问by cantera

Initial state in a Redux app can be set in two ways:

Redux 应用程序中的初始状态可以通过两种方式设置:

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

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?

如果您将初始状态传递给您的商店,您如何从商店中读取该状态并将其作为您的减速器中的第一个参数?

回答by Dan Abramov

TL;DR

Without combineReducers()or similar manual code, initialStatealways wins over state = ...in the reducer because the statepassed to the reducer isinitialStateand is notundefined, so the ES6 argument syntax doesn't get applied in this case.

With combineReducers()the behavior is more nuanced. Those reducers whose state is specified in initialStatewill receive that state. Other reducers will receive undefinedand because of thatwill fall back to the state = ...default argument they specify.

In general, initialStatewins over the state specified by the reducer. This lets reducers specify initial data that makes sense to themas default arguments, but also allows loading existing data (fully or partially) when you're hydrating the store from some persistent storage or the server.

TL; 博士

没有combineReducers()或类似的手动代码,initialState总是state = ...在减速器中获胜,因为state传递给减速器的initialState不是undefined,所以在这种情况下 ES6 参数语法不会被应用。

combineReducers()行为更微妙。状态在 中指定的那些减速器initialState将收到那个state。其他减速器将接收undefined,因此将回退到state = ...它们指定的默认参数。

一般情况下,initialState胜过减速器指定的状态。这让化简器可以指定对它们有意义的初始数据作为默认参数,而且还允许在您从某些持久存储或服务器对存储进行水合时(全部或部分)加载现有数据。

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:

那么您的减速器可能如下所示:

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 createStorewas undefined. This is the statepassed to your reducer the first time. When Redux initializes it dispatches a “dummy” action to fill the state. So your counterreducer was called with stateequal to undefined. This is exactly the case that “activates” the default argument.Therefore, stateis now 0as per the default statevalue (state = 0). This state (0) will be returned.

初始状态为零。为什么?因为第二个参数createStoreundefined. 这是state第一次传递给您的减速器。当 Redux 初始化时,它会调度一个“虚拟”动作来填充状态。所以你的counter减速器被调用state等于undefined. 这正是“激活”默认参数的情况。因此,state现在0按照默认state值 ( state = 0)。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? Because createStorewas called with 42as the second argument. This argument becomes the statepassed to your reducer along with the dummy action. This time, stateis not undefined (it's 42!), so ES6 default argument syntax has no effect.The stateis 42, and 42is returned from the reducer.

为什么这次是42,而不是0?因为createStore被称为 with42作为第二个参数。这个参数state与虚拟操作一起传递给你的减速器。这一次,state不是未定义的(它是42!),因此 ES6 默认参数语法不起作用。state42,和42从减速机返回。



Now let's consider a case where you use combineReducers().
You have two reducers:

现在让我们考虑一个使用combineReducers().
你有两个减速器:

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 })看起来像这样:

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

If we call createStorewithout the initialState, it's going to initialize the stateto {}. Therefore, state.aand state.bwill be undefinedby the time it calls aand breducers. Both aand breducers will receive undefinedas theirstatearguments, and if they specify default statevalues, those will be returned.This is how the combined reducer returns a { a: 'lol', b: 'wat' }state object on the first invocation.

如果我们在createStore没有 的情况下调用initialState,它将初始化state{}。因此,state.astate.bundefined通过它调用的时间ab减速。双方ab减速会收到undefined作为他们的state论点,如果他们指定默认state值,这些将被退回。这就是组合的 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 initialStateas the argument to createStore(). The state returned from the combined reducer combinesthe initial state I specified for the areducer with the 'wat'default argument specified that breducer chose itself.

现在我将 指定initialState为 的参数createStore()。从组合减速器返回的状态我为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, statewas specified so it didn't fall back to {}. It was an object with afield equal to 'horse', but without the bfield. This is why the areducer received 'horse'as its stateand gladly returned it, but the breducer received undefinedas its stateand thus returned its ideaof the default state(in our example, 'wat'). This is how we get { a: 'horse', b: 'wat' }in return.

在这种情况下,state已指定,因此它不会回退到{}. 它是一个a字段等于的对象'horse',但没有b字段。这就是为什么areducer 接收'horse'state并很高兴地返回它,但breducer 接收undefinedstate并因此返回的默认想法state(在我们的例子中,'wat')。这就是我们获得{ 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 undefinedas the stateargument (the easiest way to implement this is to specify the stateES6 default argument value), you're going to have a nice useful behavior for combined reducers. They will prefer the corresponding value in the initialStateobject you pass to the createStore()function, but if you didn't pass any, or if the corresponding field is not set, the default stateargument specified by the reducer is chosen instead.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. 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.

总而言之,如果你坚持 Redux 约定并在它们undefined作为state参数调用时从 reducer 返回初始状态(实现这一点的最简单方法是指定stateES6 默认参数值),你将有组合减速器的一个很好的有用行为。他们会更喜欢initialState您传递给createStore()函数的对象中的相应值,但如果您没有传递任何值,或者如果未设置相应字段,state则会选择由 reducer 指定的默认参数。这种方法很有效,因为它提供了现有数据的初始化和水化,但是如果它们的数据没有被保留,它会让各个减速器重置它们的状态。当然,您可以递归地应用此模式,因为您可以combineReducers()在许多级别上使用,甚至可以通过调用减速器并为它们提供状态树的相关部分来手动组合减速器。

回答by Emilio Rodriguez

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 知道当第一个动作进入时要传递给减速器的初始状态是什么。

The second option you mention, applies only in case you didn't pass an initial state when creating the store. i.e.

您提到的第二个选项仅适用于您在创建商店时未传递初始状态的情况。IE

function todoApp(state = initialState, action)

function todoApp(state = initialState, action)

state will only be initialised if there was no state passed by Redux

仅当 Redux 没有传递状态时才会初始化状态

回答by Nicolas

how do you read that state from the store and make it the first argument in your reducers?

你如何从存储中读取该状态并将其作为减速器中的第一个参数?

combineReducers() do the job for you. The first way to write it is not really helpfull :

combineReducers() 为您完成这项工作。编写它的第一种方法并不是很有帮助:

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

回答by Bruno Grieder

I hope this answers your request (which I understood as initializing reducers while passing intialState and returning that state)

我希望这能回答您的请求(我理解为在传递 intialState 并返回该状态时初始化减速器)

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

它的要点是if(!state)mainReducer(factory) 函数中的测试

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