React redux action for Twitch extension - reactjs

I am having some problems with the dispatch of an action under Redux.
I have applied the different tutorials to the letter, but yet this does not take, I always get an empty table in my props.
Here is my code:
Config.js
import React from "react"
import ReactDOM from "react-dom"
import { Provider } from 'react-redux'
import store from './store'
import ConfigPage from "./components/ConfigPage/ConfigPage"
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<ConfigPage />
</Provider>,
rootElement
);
Store
import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import rootReducer from './reducers'
import { fetchWalletAddress } from './actions/index'
const store = createStore(rootReducer, applyMiddleware(thunk))
store.dispatch(fetchWalletAddress());
export default store;
Reducer wallet.js
import { GET_WALLET_ADDRESS } from "../actions/actionTypes.js";
const initialState = {
wallet:[]
}
export default function(state = initialState, action){
switch(action.type){
case GET_WALLET_ADDRESS:
return [ ...state, ...action.payload];
default:
return state;
}
}
My action :
import {GET_WALLET_ADDRESS} from './actionTypes.js'
import axios from 'axios'
const apiUrl = 'https://api.elrond.com/accounts/erd15qltd5ccalm5smmgdc5wnx46ssda3p32xhsz4wpp6usldq7hq7xqq5fmn6';
export const fetchWalletAddress = () => {
return (dispatch) => {
return axios.get(apiUrl)
.then(response => {
return response.data
})
.then(data => {
dispatch({
type: GET_WALLET_ADDRESS,
payload: data
})
})
.catch(error => {
throw (error);
});
};
};
And for finish, my Configpage.js
import React from 'react'
import Authentication from '../../util/Authentication/Authentication'
import './Config.css'
import { connect } from 'react-redux'
import { fetchWalletAddress } from '../../actions/index'
class ConfigPage extends React.Component{
constructor(props){
super(props)
this.Authentication = new Authentication()
//if the extension is running on twitch or dev rig, set the shorthand here. otherwise, set to null.
this.twitch = window.Twitch ? window.Twitch.ext : null
this.state={
finishedLoading:false,
theme:'light',
isVisible:true,
wallet_address:'erd15qltd5ccalm5smmgdc5wnx46ssda3p32xhsz4wpp6usldq7hq7xqq5fmn6'
}
this.walletAddressHandler = this.walletAddressHandler.bind(this);
this.onSubmitForm = this.onSubmitForm.bind(this);
}
walletAddressHandler(event){
this.setState({
[event.target.name]:event.target.value
});
}
onSubmitForm(){
fetchWalletAddress();
this.twitch.rig.log(this.props.wallet)
}
contextUpdate(context, delta){
if(delta.includes('theme')){
this.setState(()=>{
return {theme:context.theme}
})
}
}
visibilityChanged(isVisible){
this.setState(()=>{
return {
isVisible
}
})
}
componentDidMount(){
this.twitch.rig.log(this.props.wallet)
if(this.twitch){
this.twitch.onAuthorized((auth)=>{
this.Authentication.setToken(auth.token, auth.userId)
if(!this.state.finishedLoading){
// if the component hasn't finished loading (as in we've not set up after getting a token), let's set it up now.
// now we've done the setup for the component, let's set the state to true to force a rerender with the correct data.
this.setState(()=>{
return {finishedLoading:true}
})
}
})
this.twitch.listen('broadcast',(target,contentType,body)=>{
this.twitch.rig.log(`New PubSub message!\n${target}\n${contentType}\n${body}`)
// now that you've got a listener, do something with the result...
// do something...
})
this.twitch.onVisibilityChanged((isVisible,_c)=>{
this.visibilityChanged(isVisible)
})
this.twitch.onContext((context,delta)=>{
this.contextUpdate(context,delta)
})
}
}
componentWillUnmount(){
if(this.twitch){
this.twitch.unlisten('broadcast', ()=>console.log('successfully unlistened'))
}
}
filterFloat(value) {
if (/^(\-|\+)?([0-9]+(\.[0-9]+)?|Infinity)$/
.test(value))
return Number(value);
return NaN;
}
render(){
if(this.state.finishedLoading && this.state.isVisible){
return (
<div className="App">
<div className={this.state.theme === 'light' ? 'App-light' : 'App-dark'} >
<p>Add your wallet address</p>
<input
name="wallet_address"
type="text"
onChange={this.walletAddressHandler}
value={this.state.wallet_address}>
</input>
<p>{this.props.wallet.username}</p>
<button OnClick={this.onSubmitForm}>Try it</button>
<ul>
{this.state.wallet ? String((Number(this.state.wallet.balance) * Math.pow(10, -18)).toFixed(4)) : null}
</ul>
</div>
</div>
)
}else{
return (
<div className="App">
</div>
)
}
}
}
const mapStateToProps = state => {
return {
wallet: state.wallet
}
};
export default connect(mapStateToProps, null)(ConfigPage);
thank you in advance for your help

You do not need to dispatch fetchWalletAddress while creating store, you can do that in the component. Also when you call fetchWalletAddress in the component, make sure to use the function that you make available to component via mapDispatchToProps argument of connect otherwise it wouldn't affect the redux store
Another thing you must do is to not use the updated redux value in the same function call since it takes a render cycle for it to reflect the updated change
store.js
import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import rootReducer from './reducers'
const store = createStore(rootReducer, applyMiddleware(thunk))
export default store;
Configpage.js
...
componentDidMount(){
this.props.fetchWalletAddress();
this.twitch.rig.log(this.props.wallet)
if(this.twitch){
this.twitch.onAuthorized((auth)=>{
this.Authentication.setToken(auth.token, auth.userId)
if(!this.state.finishedLoading){
// if the component hasn't finished loading (as in we've not set up after getting a token), let's set it up now.
// now we've done the setup for the component, let's set the state to true to force a rerender with the correct data.
this.setState(()=>{
return {finishedLoading:true}
})
}
})
this.twitch.listen('broadcast',(target,contentType,body)=>{
this.twitch.rig.log(`New PubSub message!\n${target}\n${contentType}\n${body}`)
// now that you've got a listener, do something with the result...
// do something...
})
this.twitch.onVisibilityChanged((isVisible,_c)=>{
this.visibilityChanged(isVisible)
})
this.twitch.onContext((context,delta)=>{
this.contextUpdate(context,delta)
})
}
}
...
componentDidUpdate(prevProps) {
if (!_.isEqual(prevProps.wallet, this.props.wallet)) {
this.twitch.rig.log(this.props.wallet)
}
}
...
onSubmitForm(){
this.props.fetchWalletAddress();// use action from props
}
...
const mapDispatchToProps = {
fetchWalletAddress,
}
const mapStateToProps = state => {
return {
wallet: state.wallet
}
};
export default connect(mapStateToProps, mapDispatchToProps)(ConfigPage);

Related

Redux-Initial Api call Not working from action creator

I have just gone through some redux tutorial and started to implement
i have one API call that has to be happen from redux as soon as page loads.. its possible with ComponentDidMount ,but i need to know how redux helps in achieving this.
For easiness i had shared the code in
https://codesandbox.io/s/quirky-sunset-s95gu?fontsize=14
My index.js look like
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { createStore,applyMiddleware } from "redux";
import allReducer from "./reducers";
import { Provider } from "react-redux";
import thunk from 'redux-thunk';
import "./styles.css";
let store = createStore(
allReducer,
applyMiddleware(thunk)
);
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
My Action creater look like(action/index.js)
import UserApi from "../UserApi";
export function loadUserSuccess(user) {
return { type: "LOAD_USER_SUCCESS", user };
}
export function loadUsers() {
return function(dispatch) {
return UserApi.getAllUsers()
.then(user => {
console.log("midhun");
dispatch(loadUserSuccess(user));
})
.catch(error => {
throw error;
});
};
}
and its subsequent api caliing function look like
class UserApi {
static getAllUsers() {
return fetch("https://jsonplaceholder.typicode.com/users")
.then(response => {
console.log("response", response);
return response.json();
})
.catch(error => {
return error;
});
}
}
export default UserApi;
My Reducer look like
import initialState from "./InitialState";
export default function IsLoggedReducer(state = initialState.user, action) {
console.log(state, action);
switch (action.type) {
case "LOAD_USER_SUCCESS":
return state;
default:
return state;
}
}
and my App.js look like
import React from "react";
import { connect } from "react-redux";
import * as userActions from "./action/index";
import UserList from "./UserList";
class App extends React.Component {
render() {
return (
<div>
<h1>MYpage</h1>
<UserList user={this.props.user} />
</div>
);
}
}
function mapStateToProps(state, ownProps) {
return {
user: state.user
};
}
export default connect(mapStateToProps)(App);
I had put couple of console in action creator and its subsequent function,but its not triggering.
Any help will be much much apreciated and will be highly help for beginners
You guys can check the complete set of code
https://codesandbox.io/s/quirky-sunset-s95gu?fontsize=14
In addition to the info lavor gaved, since you use combineReducers, you need to access to state by using your reducer key.
App.js
import React from "react";
import { connect } from "react-redux";
import {loadUsers} from "./action/index";
import UserList from "./UserList";
class App extends React.Component {
componentDidMount() {
this.props.loadUsers();
}
render() {
return (
<div>
<h1>MYpage</h1>
<UserList users={this.props.users} />
</div>
);
}
}
function mapStateToProps(state, ownProps) {
return {
users: state.IsLoggedReducer
};
}
export default connect(mapStateToProps, {loadUsers})(App);
I also made some corrections in the reducer file, we need to return the new state with the given payload.
import initialState from "./InitialState";
export default function IsLoggedReducer(state = initialState.user, action) {
console.log("ap", action.payload);
switch (action.type) {
case "LOAD_USER_SUCCESS":
return [...action.payload]
default:
return state;
}
}
And action file:
import UserApi from "../UserApi";
export function loadUserSuccess(users) {
return { type: "LOAD_USER_SUCCESS", payload: users };
}
export function loadUsers() {
return function(dispatch) {
return UserApi.getAllUsers()
.then(users => {
dispatch(loadUserSuccess(users));
})
.catch(error => {
throw error;
});
};
}
You can check this codesandbox for the working app.
https://codesandbox.io/s/cranky-colden-v6r6w
You are not dispatching your action, try to do it in componentDidMount (you need to map dispatch to props first):
App.js
componentDidMount() {
this.props.loadUsers();
}
// ...
function mapStateToProps(state, ownProps) {
return {
user: state.user
};
}
function mapDispatchToProps(dispatch) {
return {
loadUsers: () => dispatch(userActions.loadUsers())
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);

how can i dispatch store and override initial props in nextjs

I want to call API inside getInitialProps then save that response in redux store.so how can I call dispatch store and override initial props
right now after fetching data from API, I'm able to dispatch value to the reducer and it also saving data in store but after saving data my app calling for initial props(i don't know from where it changes the states) and overriding my new saved data into initial props.
main.js
class StepThree extends React.Component {
static async getInitialProps({ reduxStore, req }) {
let encID = req.query.id //null
try {
let encode = await encrption(encID,7,'dec')
const apiCall = await fetch(`${config.leadSearchApi}&search_param=${encode}`);
let res = await apiCall.json();
if(res.data['next_action'] !== "converted"){
let test = await reduxStore.dispatch({ type: PATIENT_NAME,payload:res.data.name });
console.log(test,'res');
await reduxStore.dispatch({ type: PATIENT_NUMBER,payload:res.data['mobile_number'] });
await reduxStore.dispatch({ type: LEAD_ID,payload:res.data.id });
}
} catch (err) {
console.log(err,'get err');
}
return { }
}
render() {
return <div>Hello World </div>
}
}
const mapStateToProps = (state, prevProps) =>{
return{
AmbSelect:state.StepThreeReducer.isAmbSel,
Addons:state.StepThreeReducer.addonSel,
VehcileData:state.StepThreeReducer.vehcileData,
TotalPrice:state.StepThreeReducer.totalPrice,
Cameback:state.StepThreeReducer.comeback,
LeadID:state.Main.LeadId
}
}
export default connect(mapStateToProps,{addOnSelected,priceCal,updateLeadS3,previous,VehicleDataHandler,updateVehData, addonsCount,totalPrice,existLeadData,linkLead2,linkLead3,encrption })(StepThree);
App.js
import App, { Container } from 'next/app'
import React from 'react'
import withReduxStore from '../lib/with-redux-store'
import { Provider } from 'react-redux'
class MyApp extends App {
render () {
const { Component, pageProps, reduxStore } = this.props;
return (
<Container>
<Provider store={reduxStore}>
<Component {...pageProps} />
</Provider>
</Container>
)
}
}
export default withReduxStore(MyApp)
redux-store.js
import React from 'react'
import { initializeStore } from '../store'
const isServer = typeof window === 'undefined'
const __NEXT_REDUX_STORE__ = '__NEXT_REDUX_STORE__'
function getOrCreateStore (initialState) {
// Always make a new store if server, otherwise state is shared between requests
if (isServer) {
return initializeStore(initialState)
}
// Create store if unavailable on the client and set it on the window object
if (!window[__NEXT_REDUX_STORE__]) {
window[__NEXT_REDUX_STORE__] = initializeStore(initialState)
}
return window[__NEXT_REDUX_STORE__]
}
export default App => {
return class AppWithRedux extends React.Component {
static async getInitialProps (appContext) {
// Get or Create the store with `undefined` as initialState
// This allows you to set a custom default initialState
const reduxStore = getOrCreateStore()
// Provide the store to getInitialProps of pages
appContext.ctx.reduxStore = reduxStore
let appProps = {}
if (typeof App.getInitialProps === 'function') {
appProps = await App.getInitialProps(appContext)
}
return {
...appProps,
initialReduxState: reduxStore.getState()
}
}
constructor (props) {
super(props)
this.reduxStore = getOrCreateStore(props.initialReduxState)
}
render () {
return <App {...this.props} reduxStore={this.reduxStore} />
}
}
}
store.js
import { createStore, applyMiddleware,combineReducers } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunkMiddleware from 'redux-thunk'
import './actions';
import reducer from './reducers'
export function initializeStore () {
return createStore(
reducer,
composeWithDevTools(applyMiddleware(thunkMiddleware))
)
}
reducer.js
import {PATIENT_NAME,PATIENT_NUMBER,RIDE_DATE,RIDE_TIME,FCAUSES,SETCAUSE} from '../actions/types';
const INITIAL_STATE = {
patient_name:'',
patient_number:'',
ride_date:false,
ride_time:false,
causes:{},
sel_cause:''
};
export default (state=INITIAL_STATE,action) => {
console.log(action,'reducer')
switch(action.type) {
case PATIENT_NAME:
return {...state,patient_name:action.payload};
case PATIENT_NUMBER:
return {...state,patient_number:action.payload};
case RIDE_DATE:
return {...state,ride_date:action.payload};
case RIDE_TIME:
return {...state,ride_time:action.payload};
case FCAUSES:
return {...state,causes:action.payload};
case SETCAUSE:
return {...state,sel_cause:action.payload};
default:
return state;
}
}
after dispatch I don't want to make app state as initial props
please help,stuckkkkkk
You did't provide an initialState into initializeStore when you creating your store.
To make sure that you're using the same state shape across server and client pass the initialState params to you store initialization inside store.js:
export function initializeStore(initialState = {}) {
return createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(thunkMiddleware))
)
}
Otherwise same state will always be applied and server-propagated state will be lost.

not getting synchronous data from redux thunk

action creator
export function pickup(latlng) {
return function(dispatch) {
dispatch({ type: PICKUP_STATE,payload:latlng });
};
}
Reducer
import {
PICKUP_STATE,
PICKUP_ADD,
DROPOFF_STATE
} from '../actions/types';
export default (state={},action) => {
const INITIAL_STATE = {
pickup: '',
pickupAdd:''
};
switch(action.type) {
case PICKUP_STATE:
console.log(action.payload)
return {...state,pickup:action.payload};
case PICKUP_ADD:
return{...state,pickupAdd:action.payload};
case DROPOFF_STATE:
return {...state,dropoff:action.payload}
default:
return state;
}
//return state;
}
Map component
import {
connect
} from "react-redux";
import * as actions from "../actions"
class Map extends React.Component {
componentWillReceiveProps(nextprops) {
if (nextprops.pickupProps !== undefined) {
this.setState({
pick: nextprops.pickupProps
}, () => {
console.log(this.state.pick);
});
}
}
isPickEmpty(emptyPickState) {
this.props.pickup(emptyPickState);
// setTimeout(() =>{ console.log('sdkjlfjlksd',this.state.pick)
},3000);
console.log(this.state.pick);
}
}
const mapStateToProps = (state) => {
// console.log(state.BookingData.pickup);
return {
pickupProps:state.BookingData.pickup,
pickupAddProps: state.BookingData.pickupAdd
}
}
export default connect(mapStateToProps,actions)(Map);
app.js root file
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import "normalize.css/normalize.css"
import "./styles/styles.scss";
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import reduxThunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import AppRouter from './routers/AppRouter';
import reducers from './reducers';
import {AUTH_USER} from "./actions/types";
const middleware = [
reduxThunk,
];
const store = createStore(reducers, composeWithDevTools(
applyMiddleware(...middleware),
// other store enhancers if any
));
const token = localStorage.getItem('token');
if(token){
store.dispatch({type:AUTH_USER});
}
ReactDOM.render(
<Provider store={store}>
<AppRouter />
</Provider>
, document.getElementById('app'));
here my problem is when i'm calling isPickEmpty() from my map component
it invoke action creator this.props.pickup(false) (i also checked in redux-devtools it show false value) then i'm consoling pick state( which store in componentWillReceiveProps(nextprops)) so it showing default value instead of false but when i'm consoling the value inside setTimeout(() =>{console.log('sdkjlfjlksd',this.state.pick) }, 3000); it showing false value
correct me if i'm wrong what i know that redux-thunks works in synchronous manner not asynchronous manner so here why it's not working in synchronous manner
i'm stuck,plz anyone help me!
Update
i just got where the prblm, actually in componentWillReceiveProps where i'm setting pick state value because it is asynchronous so when i'm fetching the value in isPickEmpty function i'm getting prev value.
how handle setState or is there any way to solve
At the component you use the values in BookingData, but on the reducer you add it direct to the state.
const mapStateToProps = (state) => {
console.log(state);//Check the state here
return {
pickupProps:state.pickup,
pickupAddProps: state.pickupAdd
}
}
Should work well if you se this mapStateToProps

Could not find store in either the context or props of connect site redux error

I am trying to create something similar to todo app with react and redux from here.I have been reading all the solutions for this problem and nothing seems to be working for my case.
Most of the solutions purpose using Provider which I am already using. Any help is much appreciated.
Edit - Few import statement might be missing in snippets, but all components are being imported whenever needed and actions are also defined in action file.
Index.js
import App from './components/App'
import reducer from './reducers/index'
const store = createStore(reducer)
const AppWithStore = (
<Provider store={store}>
<App />
</Provider>
)
ReactDOM.render(AppWithStore, document.getElementById('root'))
Update - Combined Reducer code
import React from 'react'
import { combineReducers } from 'redux'
import TestReducer from './TestReducer'
export default combineReducers({
TestReducer,
})
App.js
import Test from './Test';
class App extends Component {
render() {
return (
<Test />
);}
}
ReactDOM.render(
(<App/>),
document.getElementById("root")
);
export default App
Test.js contains both component and container
import { connect } from 'react-redux'
import { add } from '../actions'
const mapStateToProps = state => ({
todos: state.todos,
})
class Test extends Component {
dosomething() {
const dispatch = this.props;
dispatch(add("New Note"));
}
render() {
return (
<div>
< button OnClick = { this.dosomething.bind(this)} > Test </button>
</div>
)
}
}
export default connect(mapStateToProps)(Test)
The reducer logic for Test is as given below
import React from 'react';
const TestReducer = (state = [], action) => {
const todos = state;
const {type, payload} = action;
switch(action.type){
case 'ADD': {
return {
...state,
todos:"new todo"
}
}
}
return state;
}
export default TestReducer
You should remove
ReactDOM.render(
(<App/>),
document.getElementById("root")
); from App.js file
When you call it again in App.js a new component instance independent of others is created, That's why it is not finding store.As store is not passed to it.
You can check it here https://codesandbox.io/s/vy7wwqw570. As i had remove render api call from app.js it is working now.
you need to import your reducers so that you
if your file name is TestReducer.js
import React from 'react';
export const TestReducer = (state = [], action) => {
const todos = state;
const {type, payload} = action;
switch(action.type){
case 'ADD': {
return {
...state,
todos:"new todo"
}
}
}
return state;
}
then import it in this manner
import {TestReducer} from './TestReducer.js';
const store = createStore(TestReducer);
Try replacing todos: state.todos with todos: state.TestReducer.todos
import { connect } from 'react-redux'
import { add } from '../actions'
const mapStateToProps = state => ({
//todos: state.todos,
todos: state.TestReducer.todos
})
class Test extends Component {
dosomething() {
const dispatch = this.props;
dispatch(add("New Note"));
}
render() {
return (
<div>
< button OnClick = { this.dosomething.bind(this)} > Test </button>
</div>
)
}
}
export default connect(mapStateToProps)(Test)

Components not having the expected props, while React and Redux Dev Tools are having the expected States and Props

I am learning react-redux, so I decided to implement what I have been learning. But I am have a bug challenge. So I console.logged this.props.users from mapStateToProps function.
I believe there's something I not doing right which I don't understand. Please an explanation in other to move on. Thanks you so much for helping out.
Here is my code.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchUsers } from '../actions/userAction';
import UserList from '../components/UserList';
class UserPage extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.fetchUsers();
}
componentDidMount() {
console.log(this.props.users);
}
render() {
return (
<div>
<h2>Users Page</h2>
<UserList users={this.props.users} />
</div>
);
}
}
const mapStateToProps = state => {
return {
users: state.userReducer.users
};
};
const mapDispatchToProps = dispatch => {
return {
fetchUsers: () => dispatch(fetchUsers())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(UserPage);
So this is what I get from the chrome console - Empty arrays.
props showing empty arrays
But when I check the React DevTool and Redux DevTool, they display the expected Props and States respectively. Below are the snapshot of the dev tools
React devtool shows the correct Props
Redux devtool show the correct States and Actions
userAction.js
import axios from 'axios';
import * as types from './actionTypes';
export let fetchingUser = () => {
return {
type: types.FETCHING_USERS
};
};
export let fetchedUser = payload => {
return {
type: types.FETCHED_USER,
payload
};
};
export let fetchUser_error = () => {
return {
type: types.FETCH_USER_ERROR
};
};
export let fetchUsers = () => {
let url = 'https://eventcity.herokuapp.com/api/v1/users';
return dispatch => {
dispatch(fetchingUser());
return axios
.get(url)
.then(response => {
const users = response.data.data;
dispatch(fetchedUser(users));
})
.catch(err => {
dispatch(fetchUser_error());
});
};
};
userReducer.js
import * as types from '../actions/actionTypes';
import initialState from './initialState';
const userReducer = (state = initialState, action = {}) => {
switch (action.type) {
case types.FETCHING_USERS:
return { ...state, users: [], error: null, loading: true };
case types.FETCHED_USER:
return { ...state, users: action.payload, error: null, loading: false };
case types.FETCH_USER_ERROR:
return {
...state,
users: [],
error: { message: 'Error loading data from the API' },
loading: false
};
default:
return state;
}
};
export default userReducer;
configureStore.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import rootReducer from '../reducer/rootReducer';
const configureStore = () => {
return createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)));
};
export default configureStore;
rootReducer.js
import { combineReducers } from 'redux';
import userReducer from './userReducer';
const rootReducer = combineReducers({
userReducer
});
export default rootReducer;
I think you might want to check this
https://github.com/reactjs/react-redux/issues/129. Your problem is using componentDidMount and componentWillMount without having a better understanding of what they are used for.
The problem is not with redux, all you need to understand is that your fetchUsers request is async and componentDidMount function is only executed once after the component has rendered and it may so happen that the data is not present by the time componentDidMount function is executed and hence your console.log(this.props.users); return empty array, Log it in the render method and you will see the correct data
class UserPage extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.fetchUsers();
}
render() {
console.log(this.props.users);
return (
<div>
<h2>Users Page</h2>
<UserList users={this.props.users} />
</div>
);
}
}

Resources