mapStateToProps returns undefined values - reactjs

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.

Related

How to create a Redux store using combineReducers without initial state

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

WHY I am not able to get state from redux to my React app?

Here's my code. I want to fetch ingredients and totalPrice from redux state but when I fetch it in main app, it is giving my undefined (this.props.ingredients and this.props.totalPrice).
reducer.js
const initialState = {
ingredients: {
salad: 0,
bacon: 0,
cheese: 0,
meat: 0,
},
totalPrice: 4.0
}
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'ADD_INGREDIENTS':
break;
default:
return state;
}
};
export default reducer
Main App (Connecting with app)
......
const mapStateToProps = state => {
return {
ings: state.ingredients,
tp: state.totalPrice
}
}
export default connect(mapStateToProps)(Burgerbuilder, axios)
This is giving me undefined
console.log(this.props.ings)
console.log(this.props.tp)
Here's how it connected it with store
import {Provider} from 'react-redux'
import {createStore} from 'redux'
import reducer from './redux/reducer';
const store = createStore(reducer)
const app = (
<React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>
)
A reducer must always return a complete state. Inside case "ADD_INGREDIENTS": you break and don't return anything. So calling an action with { type: "ADD_INGREDIENTS" } will cause your whole state to be undefined. You need to return state.
Additionally, I don't understand what your are trying to do by including axios in your connect. It should be connect(mapStateToProps)(Burgerbuilder).
You did not call combineReducer function.
In your reducers directory create a file called index.js for example, then add this code to it
import ingredientsReducer from './reducer'
export default combineReducers(
{
ingredients: ingredientsReducer,
}
);
then in your main.js file, when you create a store, feed this exported combineReducers result to it.
You can read more about combineReducers here

React Native - Redux

So, im getting started with react native and redux. I'm trying to setup a basic app state, just a counter property to increment when a button is pressed.
Basically, no error is thrown but the state doesnt get updated, at least in the screen the counter keeps having the same value.
Ill try to document the code bellow.
App.js - its where I create the store and a basic reducer
import {createStore} from 'redux'
import {Provider} from 'react-redux'
import App from './src/App'
const initialState = { counter: 0 }
const reducer = (state=initialState, action) {
switch(action.type)
{
case 'INCREASE_COUNTER':
return {counter: state.counter++}
}
return state;
}
const store = createStore(reducer)
class AppProvider extends React.Component {
render() {
return (
<Provider store={store}>
<App />
</Provider>
)
}
}
export default AppProvider
./src/App.js - its where i have the View implemented
import {connect} from 'react-redux';
class App extends React.Component {
render() {
return (
<View>
<Button title="increase" onPress={this.props.increaseCounter}/>
<Text>Counter: {this.props.counter}</Text>
</View>
)
}
}
function mapStateToProps(state) {
return {counter: state.counter};
}
function mapDispatchToProps(dispatch) {
return {
increaseCounter: () => dispatch({type: 'INCREASE_COUNTER'}),
};
}
export default connect(mapStateToProps)(App);
So no error is thrown but the screen still shows Counter: 0 as I press the button.
I'm probably missing something here.
Any help would be awesome.
Thanks and happy programming!
I guess you should pass mapDispatchToProps to connect function.
export default connect(mapStateToProps, mapDispatchToProps)(App);
https://react-redux.js.org/api/connect

Redux state is undefined in mapStateToProps couldn't find the issue

I cannot find out my error. I tried lot of answers in stackoverflow but those didn't work for me.
This is my Reducer: IcmWebReducer.js
const initialState = {
invoices : [],
params: {
status: 'Pending',
_sort: 'documentInfo.dueDate',
_order: 'desc',
q: ''
}
};
const IcmWebReducer = (state = initialState, action) =>{
switch (action.type){
case 'UPDATE_INVOICES':
return Object.assign({}, state, {
invoices: action.invoices
});
case 'UPDATE_PARAMS':
return Object.assign({}, state, {
params: action.params
});
default:
return state;
}
};
export default IcmWebReducer;
This is my App.js
import React, {Component} from 'react';
import './App.scss';
import Header from './components/header/Header'
import InvoiceAudit from "./components/invoice/InvoiceAudit";
class App extends Component {
render() {
return (
<div className="App container">
<Header/>
/* <InvoiceAudit store = {this.props.store}/> */
<InvoiceAudit/>
</div>
);
}
}
export default App;
This is the index.js
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import IcmWebReducer from "./reducers/IcmWebReducer";
let store = createStore(IcmWebReducer);
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>, document.getElementById('root'));
This is the class that i am using mapStateToProps method
const mapStateToProps = (state) => {
console.log(state);
return {
invoices: state.IcmWebReducer,
}
};
This showed me undefined in console.log . but i can't find the error. I am returning the default state in my reducer also. Can anyone help me
While exporting your app, you should use connect;
export default connect(mapStateToProps)(App)
This might help solve your problem;
const ConnectedApp = connect(mapStateToProps)(App);
And in index.js;
ReactDOM.render(< Provider store={store}>< ConnectedApp /></Provider>, document.getElementById('root'));
Also in your mapStateToProps, this would suffice
const mapStateToProps = (state) => {
console.log(state);
return {
invoices: state.invoices,
//status: state.params.status -> like this
}
I believe problem might be that your invoices state is trying to take whole reducer initial state rather than just invoices empty array.
Your App component should use connect method
App component should be like below snippet
import React, {Component} from 'react';
import {connect} from "react-redux";
import './App.scss';
import Header from './components/header/Header'
import InvoiceAudit from "./components/invoice/InvoiceAudit";
class App extends Component {
render() {
return (
<div className="App container">
<Header/>
<InvoiceAudit/>
</div>
);
}
}
const mapStateToProps = state => ({invoices: state.IcmWebReducer});
export default connect(mapStateToProps)(App);

Redux reducer doesn't update store/redux-promise not resolving

So I've recently started learning Redux and now I'm trying to make my first app with it, but I've stumbled upon a problem which I cannot resolve on my own. Basically I want a user to click a button (there will be authentication) and fetch all his or hers data from Firebase and display it.
Here is my index.js:
// Dependencies
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import createHistory from 'history/createBrowserHistory';
import { ConnectedRouter, routerReducer, routerMiddleware } from 'react-router-redux';
import ReduxPromise from "redux-promise";
import ReduxThunk from 'redux-thunk';
// Reducers
import rootReducer from './reducers';
// ServiceWorker
import registerServiceWorker from './registerServiceWorker.js';
// Styles
import './styles/index.css';
// Components
import App from './containers/App.js';
const history = createHistory();
const middleware = routerMiddleware(history);
// Create store
const store = createStore(
combineReducers({
...rootReducer,
router: routerReducer
}),
applyMiddleware(ReduxThunk, middleware, ReduxPromise)
)
ReactDOM.render((
<Provider store={store}>
<ConnectedRouter history={history}>
<App />
</ConnectedRouter>
</Provider>
), document.getElementById('root'));
registerServiceWorker();
And my main container, App.js:
import React, { Component } from 'react';
import { Route, Switch, withRouter } from 'react-router-dom'
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import firebase from 'firebase';
import firebaseConfig from '../firebaseConfig.js';
// Actions
import { fetchAllMonths } from "../actions/index";
// Static components
import Nav from '../components/Nav.js';
// Routes
import CurrentMonth from '../components/CurrentMonth.js';
import AddNewMonth from '../components/AddNewMonth.js';
import Archive from '../components/Archive.js';
import Settings from '../components/Settings.js';
class App extends Component {
constructor (props) {
super(props);
this.login = this.login.bind(this);
}
componentWillMount() {
firebase.initializeApp(firebaseConfig);
firebase.auth().signInAnonymously().catch(function(error) {
var errorCode = error.code;
var errorMessage = error.message;
});
}
login() {
this.props.fetchAllMonths();
}
render() {
if (this.props.data === undefined) {
return (
<button onClick={this.login}>Login</button>
)
} else if (this.props.data !== undefined) {
return (
<main className="main-container">
<Nav user="user"/>
<Switch>
<Route exact path='/' component={CurrentMonth}/>
<Route path='/aktualny' component={CurrentMonth}/>
<Route path='/nowy' component={AddNewMonth}/>
<Route path='/archiwum' component={Archive}/>
<Route path='/ustawienia' component={Settings}/>
</Switch>
</main>
);
}
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ fetchAllMonths }, dispatch);
}
function mapStateToProps({ data }) {
return { data };
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(App))
Main action, fetchAllMonths:
// import firebase from 'firebase';
// Firebase Config
// import axios from 'axios';
export const FETCH_ALL_MONTHS = 'FETCH_ALL_MONTHS';
export function fetchAllMonths() {
/*const database = firebase.database();
const data = database.ref('/users/grhu').on('value', function(snapshot) {
return snapshot.val();
});
console.log(data) */
const data = fetch('https://my-finances-app-ef6dc.firebaseio.com/users/grhu.json')
.then(async (response) => response.json())
.then(data => {
console.log(data);
return data;
}
)
console.log(data);
return {
type: FETCH_ALL_MONTHS,
payload: data
};
};
Reducers index.js:
import { combineReducers } from "redux";
import data from "./reducer_load_from_db";
const rootReducer = combineReducers({
data: data
});
export default rootReducer;
And finally my reducer:
import { FETCH_ALL_MONTHS } from "../actions/index";
export default function(state = [], action) {
switch (action.type) {
case FETCH_ALL_MONTHS:
return [action.payload.data, ...state];
default:
return state;
}
return state;
}
So I'm sure that fetch works fine, because console.log(data) gives me a valid JSON file, but second console.log(data) with the passed const gives me a promise, which then I send as a payload to a Reducer. CreateStore also seems to work, because in the React dev console I can see a "data" prop in App container. I use redux-promise which should resolve the Promise in payload and return a JSON to the store, but data remains undefined. Also tried redux-promise-middleware, but again, no luck.
What am I missing? I've looked at that code for several hours and I cannot understand what is wrong with it.
I'll appreciate all the answers, but i really want to understand the issue, not just copy-paste a solution.
Thanks in advance!
Initial Answer
From what I'm reading in your fetchAllMonths() action creator, you're setting a property on the action it returns called payload equal to the data returned from your API call.
return {
type: FETCH_ALL_MONTHS,
payload: data
};
If you logged action.payload in your reducer like so...
switch (action.type) {
case FETCH_ALL_MONTHS:
console.log(action.payload)
return [action.payload.data, ...state];
default:
return state;
}
I believe you'd see the data returned from your API call.
So then in your reducer you would be expecting action.payload as a property of the FETCH_ALL_MONTHS action. And you'd want to use the spread operator ...action.payload.
Also, to make your logic a little easier to read, why not try using an async action to fetch data and then dispatch an action that takes in the data returned from the API call?
Hope that helps!
Updated Answer
As I thought about this and read your reply to my answer, I think you may need to use an async action to ensure your data was successfully fetched. I made a really simple CodePen example using async actions.
https://codepen.io/joehdodd/pen/rJRbZG?editors=0010
Let me know if that helps and if you get it working that way.

Resources