混淆 createStore 在 redux 中的工作方式

Posted

技术标签:

【中文标题】混淆 createStore 在 redux 中的工作方式【英文标题】:Confusion with how createStore works in redux 【发布时间】:2020-08-16 16:21:12 【问题描述】:

我在学习 Redux 时遇到了 createStore 函数。因此,据我了解 createStore 接收 3 个参数:

reducer
initial state
enhancers (for simplicity we will use only middlewares)

但是当我们在操作中使用 createStore 时,我们不会将初始状态作为第二个参数传递,而是传递具有默认状态的 reducer,如下所示:

const initialState = counter:0
const reducer =(state=initialState, action)=>...

问题是为什么我们不把初始状态作为第二个参数,而是将初始状态传递给reducer?

【问题讨论】:

【参考方案1】:

我认为您实际上将 createStore 与 reducer 混淆了。将 createStore 想象成一个函数,它通过添加特定的中间件和其他功能(如调度)返回一组 reducer

您的应用程序中通常有多个 reducer,您实际上使用 combineReducers 组合它们

比如说你 combineReducers 是

import userReducer from 'reducers/user';
import authReducer from 'reducers/auth';
import countReducer from 'reducers/count';
const reducers = combineReducers(
   userReducer,
   authReducer,
   countReducer,
);

现在 createStore 的 initialState 必须是带有 userReducer, authReducer, countReducer 键的对象的格式,然后是各个 reducers 状态。例如


   userReducer:  id: 1, name: 'Test',
   authReducer:  isLoading: true, isAuthenticated: false,
   countReducer: counter: 0

现在第二个键相当于每个减速器的 initialState

例如:reducer/count.js

const initialState = counter:0
const reducer =(state=initialState, action)=>...

它的工作方式是 createStore 实际上会在每次调用 action 时使用 action 调用 reducer,就像

reducer(state, action);

如果是 combineReducer,它的工作原理如下

const combineReducers = (reducers) => 
    return (state, action) => 
        const tempState =  ...state ;
        Object.keys(reducers).forEach((key) => 
            tempState[key] = reducers[key](tempState[key], action);
        );
        return tempState;
    ;
;

第一次调用它时使用

reducer(initialState, type: "@@redux/INIT");

这样每个reducer的初始状态都被填充了

P.S. 如果你不将 initialState 传递给 createStore,每个 reducer 都会像 const reducer =(state=initialState, action)=> 一样接受传递给它的默认参数,并返回默认 switch 子句的状态,从而导致使用每个 reducer 的 initialState

【讨论】:

【参考方案2】:

我认为您将 reducerinitial 状态与应用的全局状态混淆了。

全局状态仅表示应用中所有 reducer 的组合状态。

为简单起见,我们假设您的应用中只有一个 reducer。

减速机:

function todos(state = [], action) 
  switch (action.type) 
    case 'ADD_TODO':
      return state.concat([action.text])
    default:
      return state
  

所以这个简单的函数todos 是我们的reducer,它会在运行时为我们提供当前状态树。

所以这是createStore的第一个参数。

初始状态:

['Understanding Store']

让我们假设我们的初始状态是一个包含 1 个项目的数组,如上所示。

这将是createStore 的第二个参数。

现在我们像这样创建我们的商店:

import  createStore  from 'redux'

//... code
.
.
.
const store = createStore(todos, ['Understanding Store'])

现在我们的商店已创建。 没什么特别的,store 基本上是一个对象,上面没有什么方法。

其中一种方法是dispatch。 这个方法有助于dispatching 一个动作,它会通过我们的reducer然后更新状态。

所以当我们这样做时

store.dispatch(
  type: 'ADD_TODO',
  text: 'Learn methods on Store'
)

这将更新我们的状态如下:

['Understanding Store','Learn methods on Store']

但是当您的应用变大时,您可能希望创建不同的函数(reducer)来管理全局状态的不同部分。

如果我们还有一个 reducer,请说counter.js

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

然后,为了结合我们的第一个 reducer todos 和这个 counter reducer,我们有一个名为 combineReducer 的实用程序。

rootReducer.js

import  combineReducers  from 'redux'
import todos from './todos'
import counter from './counter'

export default combineReducers(
  todos,
  counter
)

然后使用createStore,您只需这样做:

import  createStore  from 'redux'
import rootReducer from './rootReducer.js;

const store = createStore(rootReducer);

在使用combineReducers 时,您需要遵守一些规则。

阅读规则here

【讨论】:

【参考方案3】:

createStore 中将初始状态作为第二个参数传递的用例适用于在加载应用程序时从外部获取此初始状态的用例。示例可以是在服务器上为客户端渲染的服务器端渲染应用程序生成状态,或者在加载时从本地存储恢复 redux 状态的应用程序。

当reducer函数以未定义状态调用时,应该返回单个reducer的初始值,最简单的方法是使用state的默认参数:

const reducer = (state = initialState, action) => ...

这允许您在接近定义减速器的位置定义initialState,并且当您有大量减速器时,它可以很好地与combineReducers 缩放。如果你将所有 reducer 的所有 initialState 放入一个传递给 createStore 的对象中,这将变得难以保持同步。

【讨论】:

以上是关于混淆 createStore 在 redux 中的工作方式的主要内容,如果未能解决你的问题,请参考以下文章

redux之createStore方法底层封装模拟

Redux的createStore实现

redux教程之源码解析createStore

redux中createStore方法的默认参数

Redux createStore/applyMiddleWare Q

redux初识