Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ReplaceReducer causing unexpected key error

I have a React app that dynamically loads a module, including the module's reducer function, and then calls Redux's replaceReducer to, well, replace the reducer. Unfortunately I'm getting an error of

Unexpected key "bookEntry" found in initialState argument passed to createStore. Expected to find one of the known reducer keys instead: "bookList", "root". Unexpected keys will be ignored.

where bookEntry was a key on the older reducer that's getting replaced. And starting with the bookEntry module and switching to bookList causes this inverse error

Unexpected key "bookList" found in initialState argument passed to createStore. Expected to find one of the known reducer keys instead: "bookEntry", "root". Unexpected keys will be ignored.

The code is below - un-commenting the commented code does in fact fix this, but I'm guessing it shouldn't be needed.

Am I doing something else wrong with Redux that's making this code necessary?

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
    }));
}
like image 893
Adam Rackis Avatar asked Dec 04 '15 19:12

Adam Rackis


1 Answers

In general we don’t suggest you to “clean up” the data when changing routes or loading new modules. This makes the application a little less predictable. If we’re talking about hundreds of thousands of records, then sure. Is this the volume of the data you plan to load?

If there’s just a couple of thousands of items on every page, there is no benefit to unloading them, and there are downsides associated with the complexity you add to the application. So make sure you’re solving a real problem, and not optimizing prematurely.

Now, to the warning message. The check is defined inside combineReducers(). It means that unexpected state keys will be discarded. After you removed the bookEntry reducer that managed state.bookEntry, that part of the state was no longer recognized by the new root reducer, and combineReducers() logged a warning that it’s going to be discarded. Note that this is a warning, and not an error. Your code runs just fine. We use console.error() to make warning prominent, but it didn’t actually throw, so you can safely ignore it.

We don’t really want to make the warning configurable because you’re essentially implicitly deleting part of the application state. Usually people do this by mistake, and not intentionally. So we want to warn about that. If you want to get around the warning, your best bet is to write the root reducer (currently generated by combineReducers()) by hand. It would look like this:

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

However the pattern we recommend is to keep the old reducers around.

like image 151
Dan Abramov Avatar answered Nov 01 '22 17:11

Dan Abramov