How to create a Redux store using combineReducers without initial state - reactjs

I have the following Redux store:
import {createStore} from 'redux';
import rootReducer from './reducers';
export function configureStore() {
const store = createStore(rootReducer);
return store;
};
const store = configureStore()
export default store;
This is the rootReducer created with combineReducers:
import {combineReducers} from 'redux';
import application from '../features/application/reducers';
const rootReducer = combineReducers({
application,
});
export default rootReducer;
And this is the creation of the provider:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import store from './app/store';
import { Provider } from 'react-redux';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
The problem is that I am getting the following error:
Error: The slice reducer for key "application" returned undefined during initialization. If the state passed to the reducer is undefined, you must explicitly return the initial state. The initial state may not be undefined. If you don't want to set a value for this reducer, you can use null instead of undefined.
I checked this documentation, and I can't find a solution to my problem.
EDIT
I see that the problem might be related to webpack, but I have no idea of this:
This is the code for application reducer:
import { ActionInterface } from '../generals';
import {
FETCH_APPLICATION_COMPOSITE_SUCCESS,
SET_CURRENT_APPLICATION_COMPONENT
} from './actions';
const INIT_STATE = {
applicationComposite: null,
currentApplicationComponent: null
}
export default (state=INIT_STATE, action: ActionInterface) => {
switch(action.type) {
case FETCH_APPLICATION_COMPOSITE_SUCCESS: {
return {
...state,
//#ts-ignore: Object is possibly 'undefined'
applicationComposite: action.payload.applicationComposite
}
}
case SET_CURRENT_APPLICATION_COMPONENT: {
return {
...state,
//#ts-ignore: Object is possibly 'undefined'
currentApplicationComponent: action.payload.applicationComponent
}
}
}
}

You need to add default return to your reducer
import { ActionInterface } from '../generals';
import {
FETCH_APPLICATION_COMPOSITE_SUCCESS,
SET_CURRENT_APPLICATION_COMPONENT
} from './actions';
const INIT_STATE = {
applicationComposite: null,
currentApplicationComponent: null
}
export default (state=INIT_STATE, action: ActionInterface) => {
switch(action.type) {
case FETCH_APPLICATION_COMPOSITE_SUCCESS: {
return {
...state,
//#ts-ignore: Object is possibly 'undefined'
applicationComposite: action.payload.applicationComposite
}
}
case SET_CURRENT_APPLICATION_COMPONENT: {
return {
...state,
//#ts-ignore: Object is possibly 'undefined'
currentApplicationComponent: action.payload.applicationComponent
}
}
default: return state;
}
}

Related

mapStateToProps returns undefined values

Reducer
const initialState = {
isLoadingDept:true,
isLoadingEmpDesig:true,
isLoadingEmpStatus:true,
isLoadingGroup:true
};
const isLoadingReducer = (state=initialState, action:any) =>{
switch (action.type) {
case "ISLOADING_Dept":
console.log(action.payload)
return {
isLoadingDept:action.payload
};
case "ISLOADING_EmpDesig":
return {
isloadingEmpDesig:action.payload
};
case "ISLOADING_EmpStatus":
return {
isLoadingEmpStatus:action.payload
};
case "ISLOADING_Group":
return{
isLoadingGroup:action.payload
};
default:
return state;
}
};
export default isLoadingReducer
Store
import {createStore} from 'redux'
import {combineReducers} from 'redux'
import isLoadingReducer from './reducers/isLoadingReducer'
const allReducers = combineReducers({
isLoading: isLoadingReducer
})
export default createStore(allReducers)
Index
import { render } from 'react-dom';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
import store from './redux/store'
import {Provider} from 'react-redux'
render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
Emp- file I want to use store in
function mapStateToProps(state:any){
return {
isLoadingDept: state.isLoading.isLoadingDept,
isLoadingEmpDesig: state.isLoading.isLoadingEmpDesig,
isLoadingEmpStatus: state.isLoading.isLoadingEmpStatus,
isLoadingGroup: state.isLoading.isLoadingGroup
}
}
class EmpList extends Component<any, any> {
//State and stuff
componentDidMount(){
console.log(this.props.isLoadingDept)
}
//stuff
}
export {EmpList as UnconnectedEmpList};
export default connect(mapStateToProps, {forwardRef:true})(EmpList)
console log in the last file logs undefined. I did this console log because nothing was working as it should, so I wanted to see what's going on. As you can see, I also did console log in Reducer, and it returns a value it should, so that part is working properly.
Your reducers all set one value and since the return value does not reference the original state, throw the rest of the state away, so
{
isLoadingDept:true,
isLoadingEmpDesig:true,
isLoadingEmpStatus:true,
isLoadingGroup:true
};
will become
{
isLoadingDept:true,
};
To avoid that, do
return {
...state,
isLoadingDept:action.payload
};
Also, just to make you aware of it: this is a pretty old style of Redux and nowadays we recommend writing that very differently, using the official Redux Toolkit. If you are just learning Redux, you might be following a very outdated tutorial. Please read through the official "Essentials" tutorial in that case.

How to fix Uncaught TypeError: Cannot read property 'getState' of undefined?

I am trying to use React with Redux for the frontend part with django rest framework in the backend. Got the issue getState in Provider tag in App component because of issue in store. And when i try to use the map function in the Words.js, I get error of undefined use of map. And I believe this is because of value of the array is null. Hence to fixed this error of getState.
Got this error even on including the store in Provider of App component when a reducers was not defined.
When I load a static array it does get rendered properly in the specific component.
This is Redux Store in the filename:store.js
import { createStore, applyMiddleware } from "redux";
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './reducers'
const initialState = {};
const middleware = [thunk];
const enhancer = composeWithDevTools(applyMiddleware(...middleware));
const store = createStore(
rootReducer,
initialState,
enhancer
);
export default store;
The index.js file is below
import App from './components/App'
import ReactDOM from 'react-dom';
import React from 'react';
ReactDOM.render(<App />, document.getElementById("app"));
They action types file types.js using django rest_framework to create the data.
export const GET_WORDS = "GET_WORDS";
The action file words.js
import { GET_WORDS } from "./types";
import axios from 'axios';
export const getWords = () => dispatch => {
axios.get('/api/words/')
.then(res => {
dispatch({
type: GET_WORDS,
payload: res.data
});
}).catch(err => console.log(err));
}
combined reducer file
import { combineReducers } from "redux";
import words from './words';
export default combineReducers({
words
});
The reducer file word.js
import { GET_WORDS } from '../actions/types';[enter image description here][1]
const initialState = {
words: []
}
export default function (state = initialState, action) {
switch (action.type) {
case GET_WORDS:
return {
...state,
words: action.payload
}
default:
return state;
}
}
The Component in which the words list will be called: Words.js
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { getWords } from "../../../actions/words";
export class Words extends Component {
static propTypes = {
words: PropTypes.array.isRequired,
getWords: PropTypes.func.isRequired
};
componentDidMount() {
this.props.getWords();
}
render() {
return (
<Fragment>
Hi
</Fragment>
)
}
}
const mapStateToProps = state => ({
words: state.words.words
});
export default connect(mapStateToProps, { getWords })(Words);
And finally the App component
import React, { Component, Fragment } from 'react';
import Footer from './Layout/Footer/Footer';
import Header from './Layout/Header/Header';
import WordsDashboard from './Content/Words/WordsDashboard';
import { store } from '../store';
import { Provider } from "react-redux";
import { Words } from './Content/Words/Words';
export class App extends Component {
render() {
return (
<Provider store={store}>
<Fragment>
<Header />
React Buddy
<Words />
<Footer />
</Fragment>
</Provider>
)
}
}
export default App;
Your initialState has only words prop, so when mapping it to props you have one extra words level. Try changing it to:
const mapStateToProps = state => ({
words: state.words
});
Also you need to use mapDispatchToProps for getWords, since in your current code you're missing dispatch wrapper:
const mapDispatchToProps = dispatch => ({
getWords: () => dispatch(getWords())
})
export default connect(mapStateToProps, mapDispatchToProps)(Words);

React: how to update a state property of type array of objects

I have the following state - action history:
My problem is that I expect a state with zoneCategories : Array[8757] , so I'm doing something wrong in my reducer, which is:
let setZoneCategoriesReducer = function (zoneCategories = [], action) {
switch (action.type) {
case 'SET_CATEGORIES_FROM_SERVER':
return [
...zoneCategories, action.zoneCategories
]
default:
return zoneCategories;
}
}
export default setZoneCategoriesReducer
Here is my Store:
import {applyMiddleware, compose, createStore} from 'redux'
import rootReducer from './reducers'
import logger from 'redux-logger'
import thunk from 'redux-thunk'
let finalCreateStore = compose(
applyMiddleware(thunk, logger())
)(createStore)
export default function configureStore(initialState = {zoneCategories: []}) {
return finalCreateStore(rootReducer, initialState)
}
And here my App entrypoint:
import React from 'react';
import App from './App';
import './index.css';
import { Provider } from 'react-redux';
import configureStore from './redux/store'
import { render } from 'react-dom'
let initialState = {
zoneCategories: []
}
let store = configureStore(initialState)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
Also, my root reducer:
import { combineReducers } from 'redux'
import setZoneCategoriesReducer from './setZoneCategoriesReducer'
const rootReducer = combineReducers({
zoneCategories: setZoneCategoriesReducer
})
export default rootReducer
I'm mentally blocked right now, any help?
In your setZoneCategoriesReducer function, you add action.zoneCategories as an element of the returned state, which presumably contains the 8757 elements you actually need. Spread it instead of adding directly.
let setZoneCategoriesReducer = function (zoneCategories = [], action) {
// ...
return [
...zoneCategories, ...action.zoneCategories
]
// ...
}
}

Redux-Router with immutable error

I'm trying to implement this example to get my boilerplate router/redux/immutable working:
https://github.com/sjparsons/react-router-redux-immutable
However I'm coming across an error I don't see documented elsewhere and I'm not sure where to go with it. This is the error
combineReducers.js:29 Unexpected property "0" found in previous state received by the reducer. Expected to find one of the known reducer property names instead: "todos", "routing". Unexpected properties will be ignored.
I'm also receiving this error, not sure one is a result of the other:
redux-router-init.js:69 Uncaught TypeError: Cannot read property 'toJS' of undefined
There are my reducers:
todos.js
import Immutable from 'immutable'
export default (state = Immutable.List(['Code More!']), action) => {
switch(action.type) {
case 'addTodo':
return state.push(action.todo)
default:
return state
}
}
router-reducer.js
/**
* A custom router reducer to support an Immutable store.
* See: https://github.com/gajus/redux-immutable#using-with-react-router-redux
*/
import Immutable from 'immutable';
import {
LOCATION_CHANGE
} from 'react-router-redux';
const initialState = Immutable.fromJS({
locationBeforeTransitions: null
});
export default (state = initialState, action) => {
if (action.type === LOCATION_CHANGE) {
return state.merge({
locationBeforeTransitions: action.payload
});
}
return state;
};
Here's where I initialize the new store and history:
redux-router-init.js
/* External dependencies */
import { combineReducers } from 'redux-immutable';
import Immutable from 'immutable';
import { createStore, compose, applyMiddleware } from 'redux';
import { hashHistory } from 'react-router';
import { syncHistoryWithStore } from 'react-router-redux';
import createLogger from 'redux-logger';
import DevTools from './components/DevTools';
/* Internal dependencies */
import todoReducer from './reducers/todos';
import routerReducer from './reducers/router-reducer';
////////////////////////////////////////////////
/**
* Combine reducers into root reducer and create store.
* Note thate 'combineReducers' is a redux-immutable version
*/
const rootReducer = combineReducers({
todos: todoReducer,
routing: routerReducer
})
const initialState = Immutable.List(['Code More!']);
const logger = createLogger();
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(logger),
DevTools.instrument()
)
);
/* Create enhanced history object for router */
const createSelectLocationState = () => {
let prevRoutingState, prevRoutingStateJS;
return (state) => {
console.log(state);
const routingState = state.get('routing'); // or state.routing
if (typeof prevRoutingState === 'undefined' || prevRoutingState !== routingState) {
prevRoutingState = routingState;
prevRoutingStateJS = routingState.toJS();
}
return prevRoutingStateJS;
};
};
const history = syncHistoryWithStore(hashHistory, store, {
selectLocationState: createSelectLocationState()
});
////////////////////////////////////////////////
/* Exports */
export { store, history }
And here is the index where I tie it into the router:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import {Router, Route, IndexRoute, hashHistory} from 'react-router';
import App from './components/App';
import About from './components/About';
import Todos from './components/Todos';
import DevTools from './components/DevTools';
import {store, history} from './redux-router-init';
ReactDOM.render(
<Provider store={store}>
<div>
<Router history={history}>
<Route path="/" component={App}>
<IndexRoute component={Todos}/>
<Route path="/about" component={About}/>
<Route path="/todos" component={Todos}/>
</Route>
</Router>
{ DEVELOPMENT ? <DevTools/> : ''}
</div>
</Provider>,
document.getElementById('root')
);
The full app in its current state can be located here:
https://github.com/tim37123/my-boilerplate/tree/react-redux-devtools-immutable-router
I think the error happens because of this line:
const initialState = Immutable.List(['Code More!']);
It's because immutable is expected to have a mapping of property keys while it's given List which is an indexed mapping thus the error says '0'.
Changing the line to
const initialState = Immutable.Map();
should fix the issue.
You can also do this:
const initialState = Immutable.Map({
todos: Immutable.List(),
});

How to get a resolved promise to my component with Redux Promise?

I'm making a request in my action - pulling from an API that needs to load in some data into my component. I have it start that request when the component will mount, but I can't seem to get Redux-Promise to work correctly because it just keeps returning:
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
in my dev tools when I try to console.log the value inside of my componentWillMount method.
Here's my code below:
Store & Router
import React from 'react';
import { render } from 'react-dom';
import { createStore, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise';
import { Provider } from 'react-redux';
import { Router, hashHistory } from 'react-router';
import routes from './routes';
import rootReducer from './reducers';
const store = createStore(
rootReducer,
applyMiddleware(promiseMiddleware)
);
render(
<Provider store={store}>
<Router history={hashHistory} routes={routes} />
</Provider>,
document.getElementById('root')
);
Action
import axios from 'axios';
export const FETCH_REVIEWS = 'FETCH_REVIEWS';
export const REQUEST_URL = 'http://www.example.com/api';
export function fetchReviews() {
const request = axios.get(REQUEST_URL);
return {
type: FETCH_REVIEWS,
payload: request
};
};
Reviews Reducer
import { FETCH_REVIEWS } from '../actions/reviewActions';
const INITIAL_STATE = {
all: []
};
export default function reviewsReducer(state = INITIAL_STATE, action) {
switch(action.type) {
case FETCH_REVIEWS:
return {
...state,
all: action.payload.data
}
default:
return state;
}
}
Root Reducer
import { combineReducers } from 'redux';
import reviewsReducer from './reviewsReducer';
const rootReducer = combineReducers({
reviews: reviewsReducer
});
export default rootReducer;
Component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchReviews } from '../../actions/reviewActions';
class Home extends Component {
componentWillMount() {
console.log(this.props.fetchReviews());
}
render() {
return (
<div>List of Reviews will appear below:</div>
);
}
}
export default connect(null, { fetchReviews })(Home);
Any and all help is greatly appreciated. Thank you.
Redux-promise returns a proper Promise object, so you may change your code a little bit to avoid immediate execution.
class Home extends Component {
componentWillMount() {
this.props.fetchReviews().then((whatever) => { console.log('resolved')})
}
// ...
}

Resources