Can you help me with exception Unexpected key "userName" found in preloadedState argument passed to createStore. Expected to find one of the known reducer keys instead: "default". Unexpected keys will be ignored.
I discovered this Link but it doesn't help me. I don't undestand something, maybe this part from documentation: plain object with the same shape as the keys passed to it
Can you exlain me my mistake on my example?
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from 'react-redux';
import { createStore, combineReducers, applyMiddleware } from 'redux';
import App from './containers/App.jsx';
import * as reducers from './reducers'
import types from './constants/actions';
const reducer = combineReducers(reducers);
const destination = document.querySelector("#container");
var store = createStore(reducer, {
userName : ''
});
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>,
destination
);
console.log(1)
store.subscribe( ()=> {
console.log("-------------------------")
let s = store.getState()
console.log("STATE = " + s)
for (var k in s) {
if (s.hasOwnProperty(k)) {
console.log("k = " + k + "; value = " + s[k]);
}
}
})
store.dispatch({
type: types.LOAD_USER_NAME,
userName : "Oppps1"
})
my reducer:
import types from './../constants/actions'
export default function userNameReducer (state = {userName : 'N/A'}, action) {
console.log('inside reducer');
switch (action.type) {
case types.LOAD_USER_NAME:
console.log("!!!!!!!!!!!!!!!")
console.log("action.userName = " + action.userName)
for (var k in state) {
if (state.hasOwnProperty(k)) {
console.log("k = " + k + "; value = " + state[k]);
}
}
return action.userName;
default:
return state
}
}
result in console after execution:
TLDR: stop using combineReducers and pass your reducer to createStore directly. Use import reducer from './foo' instead of import * from './foo'.
Example with default import/export, no combineReducers:
// foo.js
function reducer(state, action) { return state; }
export default reducer;
----
// index.js
import myReducer from './foo';
Example with combineReducers
// foo.js
export default (state, action) => { ... }
----
// bar.js
export default (state, action) => { ... }
----
// index.js
import foo from './foo';
import bar from './bar';
const store = createStore(combineReducers({
foo,
bar,
});
The second argument of createStore (preloaded state) must have the same object structure as your combined reducers. combineReducers takes an object, and applies each reducer that is provided in the object to the corresponding state property. Now you are exporting your reducer using export default, which is transpiled to something like module.exports.default = yourReducer. When you import the reducer, you get module.exports, which is equal to {default: yourReducer}. Your preloaded state doesn't have a default property thus redux complains. If you use import reducer from './blabla' you get module.exports.default instead which is equal to your reducer.
Here's more info on how ES6 module system works from MDN.
Reading combineReducers docs from redux may also help.
Or just:
import yesReducer from './yesReducer';
const store = createStore(combineReducers({
yesReducer,
noReducer: (state = {}) => state,
});
import * as reducers from './reducers'
is a good technique to avoid having your core code depend on the various functionalities implementations.
In order to make it work, you have to specify the name of the reducer to be the same as your store key userName. This can be achieved this way:
In the index.js that sits in the reducers folder, do
export { userNameReducer as userName } from "./UserNameReducer"
export { otherReducer as other } from "./OtherReducer"
An other way would be to directly rename the exported reducer the same as the store key.
Related
My app is simply supposed to increase the age (initialized to 20) or decrease the age if ADD button or SUBTRACT button is clicked, respectively.
I am not understanding why I keeping getting this error. I check in reducer.js to make sure to initialize the state with the 'age' property accordingly, so I am completely lost as to what might be causing this. I double checked that I had all the right dependencies, as well as made sure all the syntax is correct.
One other thing I think might be causing it is that I am not using the spread operator in my reducer.js for the line of code const newState = {state} . For some reason, vscode is not letting me use the spread operator. Everytime I do and run my app, nothing appears on screen. Is there a dependency that needs to be installed to use the spread operator?
The following is my code:
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reducer from './reducer';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
// App.js
import React from 'react';
import './App.css';
import { connect } from 'react-redux';
class App extends React.Component {
render() {
return (
<div className="App">
<div>Age: <span>{this.props.age}</span></div>
<button onClick={this.props.onAgeUp}>Add</button>
<button onClick={this.props.onAgeDown}>Sub</button>
</div>
);
}
}
const mapStateToProps = (state) => {
return { age: state.age };
};
const mapDispatchToProps = (dispatch) => {
return {
onAgeUp: () => dispatch({ type: 'ADD' }),
onAgeDown: () => dispatch({ type: 'SUBTRACT' })
};
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
// reducer.js
import { ADD, SUBTRACT } from './actionTypes';
const initialState = {
age: 20
};
const reducer = (state=initialState, action) => {
const newState = {state};
switch(action.type) {
case ADD:
newState.age+=1;
break;
case SUBTRACT:
newState.age-=1;
break;
default:
return state;
}
};
export default reducer;
Quick fix, You can use Object.assign instead of spread operator:
const newState = Object.assign({}, state);
For using spread operator you must check couple of things:
first make sure #babel/plugin-proposal-object-rest-spread dependency is in package.json, then add it to plugins of babel config file:
{
"plugins": ["#babel/plugin-proposal-object-rest-spread"]
}
for more info visit babel rest spread
I see three problems here:
You're declaring newState as {state}. This creates object within object.
Since you're declaring newState with const, it can not be changed. But you're trying to change newState in both of your case.
You're not returning anything to the state. Main idea of the redux is to update the state by returning the new state.
newState looks like this the way you define it:
{
state: {
age: 20
}
}
That's why age is undefined, it's not an immediate child property.
Eg:
const letters = { alpha: 'a' }
const spreadLetters = { letters }
spreadLetters will be a new object, with one property, "letters".
The value of this property will be an exact copy of the original letters variable.
{
letters: {
alpha: a
}
}
I have a NextJS React app that uses the next-react-wrapper (basically a HOC) on _app.tsx like so:
_app.tsx
...
import withRedux from 'next-redux-wrapper';
class Page extends App<Props> {
...
}
export default withRedux(reduxStore)(Page);
store.ts
import { applyMiddleware, createStore } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly';
import rootReducer from './reducer';
export default (
initialState: any = undefined,
) => createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware()),
);
I'm struggling to work out how to access the store outside of React such as in a simple helper function. My store.ts file exports a makeStore function which is needed (including the initial state) for the next-redux-wrapper HOC.
I could access the store in a React component and pass it to my helper functions as an argument each time but that seems messy.
Is there a way to access the store direct from non React helper function modules?
It may not be preferable, but the store can be accessed from the window using a storeKey. The default key is __NEXT_REDUX_STORE__ and using it look like this:
window.__NEXT_REDUX_STORE__.getState()
Here's where that happens
The key (storeKey) can be changed in the second options parameter passed to the withRedux function parameter. For your implementation it looks like this:
export default (
initialState: any = undefined,
{ storeKey: 'whateveryouwant' } // the name on the exposed store variable
) => createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware()),
);
I had the same issue but found the solution.
The Readme of this lib gives the example of makeStore function like this:
const makeStore = (initialState, options) => {
return createStore(reducer, initialState);
};
You need to modify it a bit
let store;
const makeStore = (initialState, options) => {
store = createStore(reducer, initialState);
return store;
};
export {store}
Now you can import the store from wherever you want.
Worked for me with TS
let store: ReturnType<typeof configStore>
const configStore = () => configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(thunkMiddleware),
devTools: process.env.NODE_ENV !== 'production',
});
export const makeStore = () => {
store = configStore();
return store;
};
export type AppStore = ReturnType<typeof makeStore>;
export type AppState = ReturnType<typeof combinedReducer>;
export type AppThunk<ReturnType = void> = ThunkAction<ReturnType, AppState, unknown, Action>;
export type AppDispatch = ReturnType<AppStore['dispatch']>;
export const wrapper = createWrapper<AppStore>(makeStore, { debug: false });
export { store };
You can create high-order function to wrap any other function with store. Here is simple example that pass store as this argument to any other function.
function withReduxFunction(store) {
return function (connectedFunction) {
return function (...args) {
connectedFunction.call(store, ...args);
}
}
}
And usage. For example we want to provide store to this function
function doSomthingWothStore(arg1, arg2) {
console.log(this); // This will be store
console.log("arg1: " + arg1 + " arg2 " + arg2);
}
Do
const funcWithStore = withReduxFunction(store)(doSomthingWothStore);
Now you can call funcWithStore and this will be equal to store.
You can play with high-order function to make it suitable for you (i.e. pass store as first argument and so on).
Also you can take a look at useDispatch and useSelector hooks from react-redux. They also should work with any function.
You can import store module wherever it is needed and directly access the store functions like store.getState(). However, you need to subscribe to store to be notified of any change in the state.
You can create store first and then return it from makeStore()
export const store = createStore(...)
const makeStore() {
return store
}
export const wrapper = createWrapper(makeStore, { debug: true })
As part of my ongoing project to learn React (I'm natively an ASP.NET guy) I've hit this issue. I have a suite of React apps in which I want to use some common UI elements, so I've attempted to break these out into a separate npm package. For the shared components themselves this has worked fine.
However, some of these components depend on redux actions to operate, so I've tried to bundle these actions and a reducer function into the external package. Here's a simplified version of my actions\index.js:
export const SNACKBAR_MESSAGE = "SNACKBAR_MESSAGE";
export const SNACKBAR_HIDE = "SNACKBAR_HIDE";
export function showSnackBarMessage(message) {
console.log('hit 1');
return (dispatch, getState) => {
console.log('hit 2');
dispatch(hideSnackBar());
dispatch({
type: SNACKBAR_MESSAGE,
message: message
});
}
}
export const hideSnackBar = () => {
type: SNACKBAR_HIDE
};
And this is reducer\index.js:
import {
SNACKBAR_MESSAGE,
SNACKBAR_HIDE
} from "../actions";
const initialState = {
snackBarMessage: null,
snackBarVisible: false
};
export default function UiReducer(state = initialState, action) {
switch(action.type) {
case SNACKBAR_MESSAGE:
return Object.assign({}, state, {
snackBarMessage: action.message,
snackBarVisible: true
});
case SNACKBAR_HIDE:
return Object.assign({}, state, {
snackBarMessages: '',
snackBarVisible: false
});
default:
return state;
}
}
This is the same code that worked fine when part of the original project. These are exported by my package's entry point file like this:
// Reducer
export { default as uiReducer } from './reducer';
// Actions
export { showSnackBarMessage as uiShowPrompt } from './actions';
export { hideSnackBar as uiHidePrompt } from './actions';
Then in my consuming project, my default reducer looks like this:
import { routerReducer } from 'react-router-redux';
import { combineReducers } from 'redux';
import { uiReducer } from 'my-custom-ui-package';
// Import local reducers
const reducer = combineReducers(
{
// Some local reducers
ui: uiReducer
}
);
export default reducer;
The problem is when I try to dispatch one of these actions imported from my external package. I include the action, e.g. import { uiShowPrompt } from "my-custom-ui-package"; and dispatch it like dispatch(uiShowPrompt("Show me snackbar")); then I see the two console messages (hit 1 and hit 2) displayed, but then the following error:
Uncaught TypeError: Cannot read property 'type' of undefined
at store.js:12
at dispatch (applyMiddleware.js:35)
at my-custom-ui-package.js:1
at index.js:8
at middleware.js:22
at store.js:15
at dispatch (applyMiddleware.js:35)
at auth.js:28
at index.js:8
at middleware.js:22
The store itself looks like this:
import { createStore, combineReducers, applyMiddleware, compose } from "redux";
import thunk from 'redux-thunk';
import { browserHistory } from "react-router";
import {
syncHistoryWithStore,
routerReducer,
routerMiddleware
} from "react-router-redux";
import reducer from "./reducer";
const loggerMiddleware = store => next => action => {
console.log("Action type:", action.type);
console.log("Action payload:", action.payload);
console.log("State before:", store.getState());
next(action);
console.log("State after:", store.getState());
};
const initialState = {};
const createStoreWithMiddleware = compose(
applyMiddleware(
loggerMiddleware,
routerMiddleware(browserHistory),
thunk)
)(createStore);
const store = createStoreWithMiddleware(reducer, initialState);
export default store;
I'm afraid I don't understand this error. I don't see what I'm doing differently other than essentially moving identical code from my local project to an npm package. Since neither the actions nor reducer actually depend on redux, my npm package doesn't itself have a dependency on react-redux. Is that a problem? If there's anything else I could share to help you help me just let me know. Like I say, I'm still fairly new to all this so clearly there's something I'm not getting right!
The problem might be in declaration of hideSnackBar function
export const hideSnackBar = () => {
type: SNACKBAR_HIDE
};
Here the function is trying to return an Object Literal from Arrow Function. This will always return undefined. As the parser doesn't interpret the two braces as an object literal, but as a block statement. Thus the error, Cannot read property 'type' of undefined as store is expecting an action with property type.
Replace code like this and see if it works.
export const hideSnackBar = () => ({
type: SNACKBAR_HIDE
});
The parentheses forces it to parse as Object Literal. Hope this helps
I had exported it like
export default userReducer();
and not like this:
export default userReducer;
Just get rid of that ()
Found out that it was case of wrong order in receiving the arguments when using redux-thunk.
// wrong argument order
const anAction = () => (getState, dispatch) => {...}
// correct one
const anAction = () => (dispatch, getState) => {...}
I am trying to initialise a basic store from a root reducer with initial state.
My root reducer
import Entity from "../api/Entity";
import { UPDATE_GROUPING } from "../constants/action-types";
import IAction from "../interfaces/IAction";
import IStoreState from "../interfaces/IStoreState";
const initialState:IStoreState = {
callsInProgress: false,
groupingOptions: ["Strategy", "User", "Date"],
groupingType: "Strategy",
numberOfCalls: 2,
items: [new Entity()],
};
const rootReducer = (state = initialState, action: IAction<object>) => {
switch (action.type) {
case UPDATE_GROUPING:
return { ...state, groupingType: action.payload};
default:
return state;
}
};
export default rootReducer;
When I create the store with the rootreducer as below
import { createStore } from 'redux';
import rootreducer from '../reducers/rootreducer';
const store = createStore(rootreducer);
export default store;
It works. The React components get initialised with the correct state for groupingType, groupingOptions etc.
However if I try and use a combineReducers() approach - even with just this single root reducer (should be identical) then when my components load, they do not have any initial state passed.
ie
import { createStore } from 'redux';
import reducers from '../reducers';
const store = createStore(reducers);
export default store;
My index.ts in the reducers folder which returns a combineReducers() call (the one which doesnt work)
import {combineReducers} from 'redux';
import rootreducer from './rootreducer';
// main reducers
export default combineReducers({
rootreducer
});
And lastly my component which hooks into redux and should import the state from the redux store
import updateGroupingType from "./actions/uiactions";
import './App.css';
import * as React from 'react';
import { connect } from 'react-redux';
import IStoreState from './interfaces/IStoreState';
interface IGroupingProps {
groupingOptions? : string[],
groupingType? : string,
updateGroupingAction? : any
}
class GroupingSelector extends React.Component<IGroupingProps, {}> {
constructor(props: IGroupingProps) {
super(props);
this.onGroupingChange = this.onGroupingChange.bind(this);
}
public render() {
if (this.props.groupingOptions == null)
{
return null;
}
return (
<div className="Grouping-selector">
<div className="Horizontal-panel-right Grouping-search-combo">
<select onChange={this.onGroupingChange}>
{this.props.groupingOptions.map((name, index)=>
<option key={index}>{name}</option>
)}
</select>
</div>
<div className="Content Horizontal-panel-right">
Group by
</div>
</div>);
}
private onGroupingChange(e: any) {
const { value } = e.target;
this.props.updateGroupingAction(value);
}
}
const mapStateToProps:any = (state: IStoreState) => {
return {
groupingOptions: state.groupingOptions,
groupingType: state.groupingType,
};
}
const mapDispatchToProps = (dispatch:any) => {
return {
updateGroupingAction: (groupingType:string) => dispatch(updateGroupingType(groupingType))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(GroupingSelector);
Why is my usage of combineReducers not working in the same way as when I use the single rootreducer?
From the doc
The combineReducers helper function turns an object whose values are different reducing functions into a single reducing function you can pass to createStore.
The resulting reducer calls every child reducer, and gathers their results into a single state object.
The state produced by combineReducers() namespaces the states of each reducer under their keys as passed to combineReducers()
When you are using rootReducer as a key inside of your combineReducers, it will create a state which shape will be
{ "rootReducer": YOUR_PREVIOUS_STATE}
You should use combineReducers only if you have different reducers for each key
Your root reducer should be key value pairs like,
export default combineReducers({
home:homeReducer
});
So that in your component, mapStateToProps() you will be able to access these values as,
const mapStateToProps = (state: any) => {
return {
users: state.home
};
};
i'm making website with React + Redux. before i added middleware, application works fine, but after added, reducer not dispatching correctly.
this is my code:
import { createStore, applyMiddleware } from 'redux';
...
let createStoreWithMiddleware = applyMiddleware(logger)(createStore);
let store = createStoreWithMiddleware(appReducer);
store.dispatch(updateText('text changed.'));
and this is my action creator just used above:
const UPDATE_TEXT = 'UPDATE_TEXT';
function updateText(text = '') {
return {
type: UPDATE_TEXT,
text
};
}
and this is appReducer:
const defaultState = { text: 'default' };
function appReducer(state = defaultState, action) {
switch(action.type) {
case UPDATE_TEXT:
return Object.assign({}, state, {
text: action.text
});
default:
return state;
}
}
if i just remove applyMiddleware and create store directly with use createStore like this:
let store = createStore(appReducer);
it works fine! but i really want to use middlewares. what should i do? anyone gimme a hand will be very appreciate!
In older versions of redux-logger (which you may be using), the initial import was a function that needed to first be called like this:
import createLogger from 'redux-logger'
const logger = createLogger()
Then you could use logger as middleware:
...
const createStoreWithMiddleware = applyMiddleware(logger)(createStore)
...
However, in the current version, you can simply import logger from 'redux-logger' and use it that way. If you want to create a logger with custom options, you can import { createLogger } from 'redux-logger'.