如何将初始状态传递给减速器

Posted

技术标签:

【中文标题】如何将初始状态传递给减速器【英文标题】:How to pass initial state to reducer 【发布时间】:2017-12-21 10:00:19 【问题描述】:

我目前使用一个将初始状态传递给它的减速器创建我的商店。

import reducers from './reducers'

const store = createStore(
    reducers,
    initialState,
    compose(...enhancers)
  )


// reducers.js
export default function reducer(state, action) 
  console.log('state', state)
  switch (action.type) 
    case 'LOAD_UI':
      return Object.assign(, state, 
        loadUI: action.loaded
      );
    case 'NO_MATCH':
      return Object.assign(, state, 
        loadUI: true,
        isNoMatch: action.isNoMatch
      );
    default:
      return state;
  

当我在 reducer 函数中记录状态时,我会得到配置存储时设置的状态:

// if we are in production mode, we get the initial state from the window object, otherwise, when we are in dev env we get it from a static file
const preloadedState = window.__INITIAL_STATE__ === '__PRELOADEDSTATE__' ? initialState : window.__INITIAL_STATE__

const store = configureStore(preloadedState)

ReactDOM.render(
  <Provider store=store>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </Provider>
, document.getElementById('root')
)

现在我想使用另一个减速器,因为我试图将 Apollo 包含在我的项目中。根据http://dev.apollodata.com/react/redux.html,我需要组合Reducers:即

combineReducers(
    todos: todoReducer,
    users: userReducer,
    apollo: client.reducer(),
  ),

所以我需要做这样的事情:

const store = createStore(
    combineReducers(
      apollo: client.reducer(),
      reducer: reducers
    ),
    initialState,
    compose(...enhancers)
  )

但是我的 reducer 函数不再能够访问状态(它是未定义的)。当我使用 combineReducers 时,如何确保它通过?

解决方案:

根据 Daniel Rarden 的建议,我需要将减速器的键与我的初始状态匹配。

我的 initialState 看起来像:


  "pages": [
    
      "id": 5,
      "title": "About",
      "slug": "about",
      "path": "/about",
      "template": "about",
      "published": "2017-07-10 02:02:30",
      "image": null,
      "seo": 
        "title": null,
        "description": null,
        "image": null,
        "fbAdmins": null,
        "gtm": null,
        "schema": ""
      ,
      "sections": [
      ]
    ,
    
      "id": 10,
      "title": "Contact",
      "slug": "contact",
      "path": "/contact",
      "template": "contact",
      "published": "2017-07-11 04:27:30",
      "image": null,
      "seo": 
        "title": null,
        "description": null,
        "image": null,
        "fbAdmins": null,
        "gtm": null,
        "schema": ""
      ,
      "sections": []
    ,
    
      "id": 4,
      "title": "Home",
      "slug": "home",
      "path": "/",
      "template": "home",
      "published": "2017-07-10 01:39:48",
      "image": null,
      "seo": 
        "title": null,
        "description": null,
        "image": null,
        "fbAdmins": null,
        "gtm": null,
        "schema": ""
      ,
      "sections": []
    
  ],
  "services": [],
  "clients": [],
  "people": [],
  "offices": [],
  "articles": [],
  "menus": 
    "header": [
      
        "title": "Home",
        "slug": "/"
      ,
      
        "title": "About",
        "slug": "/about"
      ,
      
        "title": "Contact",
        "slug": "/contact"
      
    ]
  ,
  "settings": 
    "site": 
      "title": null,
      "siteUrl": ""
    ,
    "logo": [],
    "secondarylogo": [],
    "favicon": [],
    "disclaimer": null,
    "tagline": null,
    "social": null,
    "email": null,
    "phone": null,
    "facebookAppId": "",
    "seo": 
      "title": null,
      "description": null,
      "image": null,
      "fbAdmins": null,
      "gtm": null,
      "schema": ""
    ,
    "newsLetterSignUp": 
      "image": "",
      "title": "",
      "content": ""
    ,
    "menu_settings": null
  

所以我的减速器看起来像这样:

import  combineReducers  from 'redux'
import  ApolloClient  from 'react-apollo';

const client = new ApolloClient();

import articles from './articles'
import pages from './pages'
import services from './services'
import clients from './clients'
import people from './people'
import offices from './offices'
import menus from './menus'
import settings from './settings'

export default combineReducers(
  apollo: client.reducer(),
  articles,
  pages,
  services,
  clients,
  people,
  offices,
  menus,
  settings
)

这样我的pages reducer 只能获取我的 initialState 的页面切片。

【问题讨论】:

【参考方案1】:

这没有像您预期的那样工作的原因是您更改了状态的结构,以及传递给原始减速器的内容。你的商店以前是这样的:


 loadUI: true
 isNoMatch: false

现在,您基本上已经告诉 Redux 寻找:


 apollo: 
   // ✁
 
 reducers: 
   loadUI: true
   isNoMatch: false
 

当你使用 combineReducers 时,你实际上是在为你的 state 创建隔离域——而不是将整个 state 传递给每个 reducer,redux 只会将 state 的一部分传递给每个 reducer,而 reducer 只能改变那部分状态。通过如上所示构建您的 store,您已经告诉 redux 仅将状态的 apollo 切片传递给 apollo reducer...并将状态的 reducers 部分传递给您的原始 reducer。

我猜你没有对preloadedState 进行任何更改。所以发生的事情是 redux 在 preloadedState 中寻找一个名为 reducers 的属性并将其传递给您的减速器。找不到,所以传入 undefined。

这里最简单的解决方法是,首先,选择比reducers 更具描述性的东西——也许ui?相应地更改您的 combineReducers。然后更新您的preloadedState,以便您拥有的任何初始状态都嵌套在ui 中。然后它应该按预期工作。请记住,您还需要更新选择器和/或 mapStateToProps 函数!

编辑:您可能想了解更多关于 combineReducers 的工作原理here。

【讨论】:

谢谢。这值得一读combineReducers 的工作原理。我错过了每个减速器都映射到预加载状态中的键的事实。

以上是关于如何将初始状态传递给减速器的主要内容,如果未能解决你的问题,请参考以下文章

Redux Action 没有被传递给 reducer

reactjs - 如何在formik中将状态值传递给初始值

将 props 传递给 redux 初始状态

ReactJS:为啥将组件初始状态传递给一个反模式?

当反应原生应用程序启动时,将初始状态从 API 调用传递给 createStore

为啥我的 Redux reducer 认为我的状态是未定义的?