ReplaceReducer 导致意外的键错误

Posted

技术标签:

【中文标题】ReplaceReducer 导致意外的键错误【英文标题】:ReplaceReducer causing unexpected key error 【发布时间】:2016-03-09 19:50:08 【问题描述】:

我有一个 React 应用程序,它动态加载一个模块,包括模块的 reducer 函数,然后调用 Redux 的 replaceReducer 来替换 reducer。不幸的是,我收到了一个错误

在传递给 createStore 的 initialState 参数中发现了意外的键“bookEntry”。预计会找到已知的 reducer 键之一:“bookList”、“root”。意外的键将被忽略。

bookEntry 旧reducer上的一个键被替换。从 bookEntry 模块开始并切换到 bookList 会导致此反向错误

在传递给 createStore 的 initialState 参数中发现了意外的键“bookList”。预计会找到已知的 reducer 键之一:“bookEntry”、“root”。意外的键将被忽略。

代码如下 - 取消注释注释代码实际上可以解决此问题,但我猜它不应该是必需的。

我是不是在 Redux 上做错了什么,才需要这段代码?

function getNewReducer(reducerObj)
    if (!reducerObj) return Redux.combineReducers( root: rootReducer );

    //store.replaceReducer(function()
    //    return 
    //        root: rootReducer()
    //    
    //);

    store.replaceReducer(Redux.combineReducers(
        [reducerObj.name]: reducerObj.reducer,
        root: rootReducer
    ));

【问题讨论】:

在动态加载代码时,有什么特别的原因要移除之前的reducer吗?我不太明白。一般来说,你希望旧的减速器留下来,而不是被移除。 @DanAbramov - 嗯,没有具体原因。我只是假设传出模块会自行“清理”。这不是我在实践中应该如何做的吗?我应该只触发一个调度以清除其数据,但离开减速器吗? “清理”的目的是什么?通常,您只需保留数据以防用户返回此页面。 @DanAbramov 在尝试像 React 这样的新东西时,我通常会尝试模拟一些 big 的东西,比如我朝九晚五的工作,所以我可以看看它是如何被使用的在“现实生活”中工作(而不是 ToDo)。如果我们保留一切,从日程安排、计费、联系人管理器、任务、文件等,事情很快就会失控。清除我习惯工作的应用程序类型的必要条件与 -- centralreach.com 如果你很好奇(莫名其妙为什么面向公众的网站没有应用程序的屏幕截图 - 它非常清晰) 【参考方案1】:

一般我们建议您在更改路线或加载新模块时“清理”数据。这会使应用程序的可预测性降低。如果我们谈论的是 数十万 条记录,那么当然可以。这是您计划加载的数据量吗?

如果每个页面上只有几千个项目,则卸载它们没有任何好处,并且与您添加到应用程序的复杂性相关的缺点是。因此,请确保您解决的是真正的问题,而不是过早地进行优化。

现在,到警告信息。检查在combineReducers() 中定义。这意味着意外的状态键将被丢弃。在您删除管理 state.bookEntrybookEntry 减速器后,新的根减速器不再识别该部分状态,并且 combineReducers() 记录了将被丢弃的警告。 请注意,这是一个警告,而不是错误。您的代码运行良好。我们使用console.error() 来突出警告,但它实际上并没有抛出,所以你可以放心地忽略它。

我们真的不想让警告可配置,因为您实际上是在隐式删除部分应用程序状态。通常人们会错误地这样做,而不是故意的。所以我们要对此提出警告。如果您想绕过警告,最好的办法是手动编写根减速器(当前由combineReducers() 生成)。它看起来像这样:

// I renamed what you called "root" reducer
// to "main" reducer because the root reducer
// is the combined one.
let mainReducer = (state, action) => ...

// This is like your own combineReducers() with custom behavior
function getRootReducer(dynamicReducer) 
  // Creates a reducer from the main and a dynamic reducer
  return function (state, action) 
    // Calculate main state
    let nextState = 
      main: mainReducer(state.main, action)
    ;

    // If specified, calculate dynamic reducer state
    if (dynamicReducer) 
      nextState[dynamicReducer.name] = dynamicReducer.reducer(
        nextState[dynamicReducer.name],
        action
      );
    

    return nextState;
  ;


// Create the store without a dynamic reducer
export function createStoreWithoutDynamicReducer() 
  return Redux.createStore(getRootReducer());


// Later call this to replace the dynamic reducer on a store instance
export function setDynamicReducer(store, dynamicReducer) 
  store.replaceReducer(getRootReducer(dynamicReducer));

但是我们推荐的模式是keep the old reducers around。

【讨论】:

谢谢丹 - 根据法律,我必须再等 17 个小时才能获得赏金 :) 非常感谢您提供的信息。我战斗公用事业的日子已经过去了;我很乐意留下旧的减速器,如果确实需要,只需清除过多的数据。您是否有机会看到将其中一些信息写入文档的方法?这几乎是 redux 的 唯一 部分,其中文档有点有限。 @Adam 我现在真的没有时间为文档做贡献。需要一个全面的“动态代码加载”配方,但有人需要拥有它,而我不能。 完全不用担心。你为开发社区做了很多。在我花更多时间用它来更好地理解事情之后,你会接受一些关于 replaceReducer 的更新文档的 PR 吗? @Adam 是的,但我们尽量保持 API 文档简洁。配方会是更正确的形式。

以上是关于ReplaceReducer 导致意外的键错误的主要内容,如果未能解决你的问题,请参考以下文章

少女时代目的地导致意外键错误

找出导致可怕的“网站遇到意外错误。请稍后再试”的原因。信息

如何解决由于强制展开导致的致命意外零错误[重复]

在 heroku 上部署 MERN 会导致意外错误

克隆 git repo 导致错误 - 主机密钥验证失败。致命:远端意外挂断

Selenium Firefox webdriver 导致错误:服务 geckodriver 意外退出。状态码是:2