阅读商店在Redux Reducer中的初始状态(Read Store's Initial State in Redux Reducer)

编程入门 行业动态 更新时间:2024-10-27 17:17:45
阅读商店在Redux Reducer中的初始状态(Read Store's Initial State in Redux Reducer)

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

作为第二个参数传递给createStore ( docs link ) 作为您的(子)reducers( docs链接 )的第一个参数传递它

如果您将初始状态传递到您的商店,那么您如何从商店中读取该状态,并将其作为reducer中的第一个参数?

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

pass it as the second argument to createStore (docs link) pass it as the first argument to your (sub-)reducers (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

如果没有combineReducers()或类似的手动代码,则initialState总是在reducer中胜过state = ... ,因为传递给reducer的state initialState , 并且不是 undefined ,因此在这种情况下不会应用ES6参数语法。

使用combineReducers() ,行为更细微。 那些在initialState指定状态的reducers将会收到该state 。 其他的reducer将会收到undefined ,因为这将会回到state = ...它们指定的默认参数。

一般来说, initialState胜过reducer指定的状态。 这使得reducer可以将初始数据指定为默认参数,但是当您从某些持久存储或服务器对存储进行保湿时,也允许加载现有数据(完全或部分)。

首先考虑一个你有一个减速器的情况。 说你不要使用combineReducers() 。

那么你的reducer可能看起来像这样:

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

现在我们假设你创建一个商店。

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

初始状态为零。 为什么? 因为createStore的第二个参数是undefined 。 这是第一次传递给您的reducer的状态。 当Redux初始化时,会发出一个“虚拟”动作来填满状态。 所以你的减速器被调用state等于undefined 。 这正是“激活”默认参数的情况。 因此,根据默认state值( state = 0 ), state现在为state = 0 。 此状态( 0 )将被返回。

让我们考虑一个不同的情况:

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

为什么是42 ,而不是0 ,这次? 因为createStore是以42作为第二个参数调用的。 该参数成为与虚拟动作一起传递给您的reducer的state 。 这一次, state不是未定义(它是42 !),所以ES6默认参数语法没有任何效果。 state为42 ,从还原器返回42 。


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

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

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

如果我们在没有initialState情况下调用createStore ,那么将将state初始化为{} 。 因此, state.a和state.b将在被称为a和b reducer的时候被undefined 。 a和b reducer都将接收undefined state参数,如果它们指定默认state值,则返回它们。 这是在第一次调用时组合的reducer如何返回{ a: 'lol', b: 'wat' }状态对象。

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

让我们考虑一个不同的情况:

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

现在我将initialState指定为createStore()的参数。 从组合式减速器返回的状态将为reducer指定的初始状态与指定为b reducer选择自身的'wat'默认参数相结合

让我们回想一下减速机的组合:

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

在这种情况下, state被指定,因此它不会回退到{} 。 这是a字段等于'horse' ,但没有b字段的对象。 这就是为什么a减速机接受'horse'作为它的state并且乐意地返回它,但是b减速器被收到undefined为它的state ,从而返回了的默认state 想法 (在我们的例子中, 'wat' )。 这就是我们如何得到{ a: 'horse', b: 'wat' }作为回报。


总结一下,如果您坚持使用Redux约定,并且在undefined的state参数(最简单的方法是指定state ES6默认参数值)的情况下,从reducer返回初始状态,那么您将将对于组合的reducer有一个很好的有用的行为。 他们更喜欢传递给createStore()函数的initialState对象中的相应值,但是如果没有传递任何值,或者如果没有设置相应的字段,则会选择reducer指定的默认state参数。 这种方法运行良好,因为它提供了现有数据的初始化和水合,但是如果数据未被保留,则可以让各个reducer重置其状态。 当然,您可以递归地应用此模式,因为您可以在多个层次上使用combineReducers() ,甚至可以通过调用reducer手动组合reducer并将其赋予状态树的相关部分。

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.

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

In general, initialState wins over the state specified by the 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.

First let's consider a case where you have a single reducer. Say you don't use 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 createStore was undefined. This is the state passed to your reducer the first time. When Redux initializes it dispatches a “dummy” action to fill the state. So your counter reducer was called with state equal to undefined. This is exactly the case that “activates” the default argument. Therefore, state is now 0 as per the default state value (state = 0). This state (0) will be returned.

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 createStore was called with 42 as the second argument. This argument becomes the state passed to your reducer along with the dummy action. This time, state is not undefined (it's 42!), so ES6 default argument syntax has no effect. The state is 42, and 42 is returned from the reducer.


Now let's consider a case where you use 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:

// 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 {}. Therefore, state.a and state.b will be undefined by the time it calls a and b reducers. Both a and b reducers will receive undefined as their state arguments, and if they specify default state values, those will be returned. This is how the combined reducer returns a { a: 'lol', b: 'wat' } state object on the first invocation.

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(). 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.

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 {}. It was an object with a field equal to 'horse', but without the b field. 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'). This is how we get { a: 'horse', b: 'wat' } in return.


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

更多推荐

本文发布于:2023-07-19 20:43:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1186688.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:商店   状态   Reducer   Redux   State

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!