Connect react-redux to stateless component. Properties inside the function are not updated - react-native

Problem:
I have very simple todo app. There is one action - add todo. When I add a task, I simulate sending it to the server using a setTimeout.
When I receive a response from the server, I immediately check to see if there is an error to avoid further action. In stateful component, everything works, and in stateless component it doesn't.
See the code to better understand the problem.
Environment:
"react": "16.8.6",
"react-native": "0.60.5",
"react-redux": "^7.1.1",
"redux": "^4.0.4",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.3.0"
№ 1. Stateful component:
import React, {Component} from 'react';
import {View, Button, ActivityIndicator} from 'react-native';
import {connect} from 'react-redux';
import {addTodo as addTodoAction} from '../redux/reducer';
class MainScreen extends Component {
todoGenerator = () => ({
id: new Date().getTime(),
text: 'Pls help me ' + new Date().getTime(),
});
sendTodoToServer = async () => {
const todo = this.todoGenerator();
const {addTodo} = this.props;
await addTodo(todo);
// this
const {error} = this.props;
if (error) {
console.log('error', error);
}
};
render() {
const {isLoading} = this.props;
return (
<View>
<Button title="Generate todo" onPress={this.sendTodoToServer} />
{isLoading && <ActivityIndicator />}
</View>
);
}
}
export default connect(
state => ({
todos: state.todos,
error: state.error,
isLoading: state.isLoading,
}),
{
addTodo: addTodoAction,
},
)(MainScreen);
№ 1. Stateful component. Console:
As you can see,
const {error} = this.props;
if (error) {
console.log('error', error);
}
it's work. Okay, let's move on to functional components
№ 2. Stateless component with redux connect:
import React from 'react';
import {ActivityIndicator, Button, View} from 'react-native';
import {connect} from 'react-redux';
import {addTodo as addTodoAction} from '../redux/reducer';
const MainScreenFC = ({isLoading, addTodo, error}) => {
const todoGenerator = () => ({
id: new Date().getTime(),
text: 'Pls help me ' + new Date().getTime(),
});
const sendTodoToServer = async () => {
const todo = todoGenerator();
await addTodo(todo);
if (error) {
console.log('error', error);
}
};
return (
<View>
<Button title="Generate todo" onPress={sendTodoToServer} />
{isLoading && <ActivityIndicator />}
</View>
);
};
export default connect(
state => ({
todos: state.todos,
error: state.error,
isLoading: state.isLoading,
}),
{
addTodo: addTodoAction,
},
)(MainScreenFC);
№ 2. Stateless component with redux connect. Console:
The error did not display in the console, although it is in the reducer
№ 3. Stateless component with redux HOOKS:
import React from 'react';
import {ActivityIndicator, Button, View} from 'react-native';
import {connect, shallowEqual, useDispatch, useSelector} from 'react-redux';
import {addTodo as addTodoAction} from '../redux/reducer';
const MainScreenReduxHooks = () => {
const todos = useSelector((state: AppState) => state.todos, shallowEqual);
const error = useSelector((state: AppState) => state.error, shallowEqual);
const isLoading = useSelector(
(state: AppState) => state.isLoading,
shallowEqual,
);
const dispatch = useDispatch();
const todoGenerator = () => ({
id: new Date().getTime(),
text: 'Pls help me ' + new Date().getTime(),
});
const sendTodoToServer = async () => {
const todo = todoGenerator();
await dispatch(addTodoAction(todo));
if (error) {
console.log('error', error);
}
};
return (
<View>
<Button title="Generate todo" onPress={sendTodoToServer} />
{isLoading && <ActivityIndicator />}
</View>
);
};
export default connect(
state => ({
todos: state.todos,
error: state.error,
isLoading: state.isLoading,
}),
{
addTodo: addTodoAction,
},
)(MainScreenReduxHooks);
№ 3. Stateless component with redux HOOKS. Console:
It's the same here, as in the second example.
Questions:
Can redux be connected to a stateless component?
How do you make the second and third example work the same way as the first?
Other code:
App.js
import React from 'react';
import {Provider} from 'react-redux';
import {MainScreen, MainScreenFC, MainScreenReduxHooks} from './src/screens';
import store from './src/redux';
const App = () => {
return (
<Provider store={store}>
<MainScreenFC />
</Provider>
);
};
export default App;
store.js:
import {applyMiddleware, createStore} from 'redux';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import rootReducer from './reducer';
export default createStore(rootReducer, applyMiddleware(thunk, logger));
reducer.js:
const ADD_TODO_REQUEST = 'ADD_TODO_REQUEST';
const ADD_TODO_SUCCESS = 'ADD_TODO_SUCCESS';
const ADD_TODO_FAILURE = 'ADD_TODO_FAILURE';
const initialState = {
todos: [],
isLoading: false,
error: undefined,
};
export const addTodo = data => async dispatch => {
dispatch({
type: ADD_TODO_REQUEST,
payload: {
isLoading: true,
},
});
try {
const todo = await new Promise((resolve, reject) => {
setTimeout(() => {
reject('Ooops, error');
}, 3000);
});
dispatch({
type: ADD_TODO_SUCCESS,
payload: {
todo,
isLoading: false,
},
});
} catch (e) {
dispatch({
type: ADD_TODO_FAILURE,
payload: {
isLoading: false,
error: e,
},
});
}
};
export default function(state = initialState, {type, payload}) {
switch (type) {
case ADD_TODO_REQUEST: {
return {
...state,
isLoading: true,
};
}
case ADD_TODO_SUCCESS: {
return {
...state,
isLoading: false,
todos: [...state.todos, payload.todo],
};
}
case ADD_TODO_FAILURE: {
return {
...state,
isLoading: false,
error: payload,
};
}
default:
return state;
}
}

I think the problem with your code is that you try to wait for a response in the component, it's a bad idea for both stateful and stateless components. What would I recommend you to do, is to handle error somewhere in your middleware (redux-thunk, redux-saga, etc). In this case, your component should just represent data, and if you have to display an error, just take it from props, I believe it is stored somewhere in redux.
In any way, stateless components shouldn't be an async function, because the result of an async function is a promise but not a component. There are some libraries like async-reactor, but personally, I prefer to go with another approach.
If you tell me your use case, what would you like to do once you got an error, I'll give you a more useful answer.
Update:
export const addTodo = data => dispatch => {
// tell your application that request is sending,
// so you can handle it in UI (show a progress indicator)
dispatch({
type: ADD_TODO_REQUEST,
payload: data
});
try {
const response = await createTodo(data);
dispatch({
type: ADD_TODO_SUCCESS,
payload: response
});
// here you can dispatch navigation action as well
} catch (error) {
dispatch({
type: ADD_TODO_FAILURE,
error
});
// and here you can dispatch action with a toast
// to notify users that something went wrong
}
};

Related

useEffect not triggered

I'm trying to use redux with useEffect to update/get the redux state but useEffect is totally not running at all but I have no idea what is going on. I can't even get the "hi"
import { useSelector, useDispatch } from 'react-redux';
import { setDisplayLogsheet, getLogsheets } from '../redux/actions';
...
const { displayLogsheet, logsheets } = useSelector(state => state.logsheetReducer);
const dispatch = useDispatch();
useEffect(() => {
console.log("hi")
dispatch(getLogsheets());
dispatch(setDisplayLogsheet(logsheets));
}, []);
Any help please? Thanks
UPDATE: here's more code to understand
App.js:
I have added the store inside the provider
const Stack = createStackNavigator();
export default function App() {
return(
<Provider store={Store}>
<NavigationContainer>
...
<Provider />
}
home.js:
tried to useSelector to get the logsheets and displayLogsheets and useEffect to dispatch, but the the useEffect is totally not running
export default function Home({navigation}) {
const { displayLogsheet, logsheets } = useSelector(state => state.logsheetReducer);
const dispatch = useDispatch();
useEffect(() => {
console.log('getting logsheets...')
dispatch(getLogsheets())
}, [dispatch])
useEffect(() => {
console.log('setting displayLogsheet...')
if(logsheets){
dispatch(setDisplayLogsheet(logsheets))
}
}, [dispatch, logsheets])
console.log(logsheets)
console.log(displayLogsheet)
return (
<>
<SafeAreaView>
<ScrollView>
<HomeTopStack logsheet={displayLogsheets} iterateDocket={iterateDocket} />
<ScanBarcodeButton navigation={navigation} />
{displayLogsheets.data.DO.map(logsheet => (
<TouchableOpacity onPress={() => navigation.navigate('Details', logsheet)}>
<DOCards logsheet={displayLogsheets} />
</TouchableOpacity>
))}
</ScrollView>
</SafeAreaView>
</>
)
}
store.js:
import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import logsheetReducer from './reducers';
const rootReducer = combineReducers({ logsheetReducer });
export const Store = createStore(rootReducer, applyMiddleware(thunk));
reducer.js:
this is the reducer to set display logsheet and also to get the dummy logsheet data
import { SET_DISPLAY_LOGSHEET, GET_LOGSHEETS } from "./actions";
const initialState = {
logsheets: {},
displayLogsheet: {},
}
function logsheetReducer(state = initialState, action) {
switch (action.type) {
case SET_DISPLAY_LOGSHEET:
console.log("inside logsheetReducer, SET_DISPLAY_LOGSHEET")
return { ...state, displayLogsheet: action.payload };
case GET_LOGSHEETS:
console.log("inside logsheetReducer, GET_LOGSHEET")
return { ...state, logsheets: action.payload };
default:
return state;
}
}
export default logsheetReducer;
actions.js:
import CreateFakeLogsheets from "../data/logsheet";
export const SET_DISPLAY_LOGSHEET = 'SET_DISPLAY_LOGSHEET';
export const GET_LOGSHEETS = 'GET_LOGSHEETS';
const logsheets = CreateFakeLogsheets(2,3)
export const getLogsheets = () => {
console.log("inside getLogsheets")
try {
return dispatch => {
dispatch({
type: GET_LOGSHEETS,
payload: logsheets
})
}
} catch (error) {
console.log(error)
}
}
export const setDisplayLogsheet = displayLogsheet => {
console.log("inside setDisplayLogsheets")
return dispatch => {
dispatch({
type: SET_DISPLAY_LOGSHEET,
payload: displayLogsheet
});
}
};
here's most of the code with redux and also the useEffect. Any help please
Without knowing how the rest of the code is structured, I would split the effect in two, like this:
useEffect(() => {
console.log('getting logsheets...')
dispatch(getLogsheets())
}, [dispatch])
useEffect(() => {
console.log('setting displayLogsheet...')
if(logsheets){ // only dispatch this if logsheets have been fetched
dispatch(setDisplayLogsheets(logsheets))
}
}, [dispatch, logsheets])

i can't connect redux with react-native app

I have a problem with redux and react-native because I can't connect the state of the "login" view.
I would like to upload the user data and the token in the store.
In "AppState" View, I manage the actions :
import * as type from "../redux/actionTypes";
import {initialState} from "../redux/initialState";
export default function LoginStateReducer(state = initialState, action) {
switch( action.type ) {
case type.SET_LOGIN_STATE:
return {
...state,
user: action.user,
token: action.token,
isLoading: false,
isLoggedIn: true,
};
case type.SET_LOGOUT_STATE:
return {
...state,
user: null,
token: null,
isLoading: false,
isLoggedIn: false,
};
default:
return state;
}
};
In "AppView", I manage the state :
import React, { useState, useEffect, useReducer, useMemo } from 'react';
import { View, ActivityIndicator } from 'react-native';
import AsyncStorage from '#react-native-async-storage/async-storage';
import Login from './login/LoginViewContainer';
import { AuthContext } from './config/Context';
import * as type from '../redux/actionTypes'
import { initialState } from '../redux/initialState';
import LoginStateReducer from './AppState';
import Navigator from './navigation/Navigator';
const App = () => {
const [isLoading, setIsLoading] = useState(true);
const [loginState, dispatch] = useReducer(LoginStateReducer, initialState);
const authContext = useMemo(() => ({
signIn: async(data) => {
setIsLoading(false);
const token = String(data.token);
const user= data.user;
try {
await AsyncStorage .setItem('token', token);
} catch(e) {
console.log(e);
}
dispatch({ type: type.SET_LOGIN_STATE, user: user, token: token });
},
signOut: async() => {
setIsLoading(false);
try {
await AsyncStorage .removeItem('token');
} catch(e) {
console.log(e);
}
dispatch({ type: type.SET_LOGOUT_STATE });
},
}), []);
if( loginState.isLoading ) {
return(
<View style={{flex:1,justifyContent:'center',alignItems:'center'}}>
<ActivityIndicator size="large" />
</View>
);
}
return (
<AuthContext.Provider value={authContext}>
{ loginState.token !== null ? (
<Navigator onNavigationStateChange={() => {}} uriPrefix="/app" />
)
:
<Login />
}
</AuthContext.Provider>
);
}
export default App;
In "AppViewContainer", I connect the reducer with the state :
import { connect } from 'react-redux';
import { compose, lifecycle } from 'recompose';
import { Platform, UIManager } from 'react-native';
import AppView from './AppView';
import LoginStateReducer from './AppState';
export default compose(
connect(
state => ({
user: state.user,
token: state.token,
}),
dispatch => ({
LoginStateReducer: () => dispatch(LoginStateReducer()),
})
),
lifecycle({
componentDidMount() {
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental &&
UIManager.setLayoutAnimationEnabledExperimental(true);
}
this.props.LoginStateReducer();
},
}),
)(AppView);
In the "reducer", I have :
import { combineReducers } from 'redux';
// ## Generator Reducer Imports
import LoginStateReducer from '../modules/AppState';
export default combineReducers({
login: LoginStateReducer,
});
When I call the store once connected, it has not changed and I don't understand why :
"login": {"isLoading": true, "isLoggedIn": false, "token": "", "user": ""}
Can you tell me if i am missing something?
Thank you in advance for your feedback

I am using redux in react native application to fetch and display data but its not updating on data change from backend

I am using Redux in my React-Native application.
I am fetching the data from api call and on success rendoring it on ListItem.
I am able to fetch and display data but data is not auto updating unless and until I revisit the page.
Even values are not storing into the app
I am calling method from actions in constructor and componentDidMount method.
Can you Please check the code and tell me where am I going wrong.
Here is action.js
import {
FETCHING_PRODUCT_REQUEST,
FETCHING_PRODUCT_SUCCESS,
FETCHING_PRODUCT_FAILURE
} from './types';
export const fetchingProductRequest = () => ({
type : FETCHING_PRODUCT_REQUEST
});
export const fetchingProductSuccess = (json) => ({
type : FETCHING_PRODUCT_SUCCESS,
payload : json
});
export const fetchingProductFailure = (error) => ({
type : FETCHING_PRODUCT_FAILURE,
payload : error
});
export const fetchProduct = () => {
return async dispatch => {
dispatch(fetchingProductRequest());
try {
let response = await fetch("http://phplaravel-325095-1114213.cloudwaysapps.com/api/shop/shop");
let json = await response.json();
dispatch(fetchingProductSuccess(json));
} catch(error) {
dispatch(fetchingProductFailure(error));
}
}
}
My reducer.js
import {
FETCHING_PRODUCT_REQUEST,
FETCHING_PRODUCT_SUCCESS,
FETCHING_PRODUCT_FAILURE
} from './../actions/types';
const initialState = {
loading : false,
errorMessage : '',
shops: []
}
const products = ( state = initialState, action ) => {
switch(action.type) {
case FETCHING_PRODUCT_REQUEST :
return { ...state, loading: true} ;
case FETCHING_PRODUCT_SUCCESS :
return { ...this.state, loading: false, shops: action.payload };
case FETCHING_PRODUCT_FAILURE :
return { ...state, loading: false, errorMessage: action.payload};
}
};
export default products;
product.js
import * as React from 'react';
import { FlatList , ActivityIndicator} from 'react-native';
import { ListItem } from 'react-native-elements';
import { fetchProduct } from './../../actions/products';
import { connect } from 'react-redux';
import propTypes from 'prop-types';
class Product extends React.Component {
constructor(props) {
super(props);
this.props.fetchProduct();
this.state = {
loading : true,
shops : '',
isFetching: false,
active : true,
}
}
fetchProducts() {
const shopid = 13;
fetch(`http://phplaravel-325095-1114213.cloudwaysapps.com/api/shop/shop`)
.then(response => response.json())
.then((responseJson)=> {
this.setState({
loading: false,
shops: responseJson
})
alert(JSON.stringify(this.state.shops));
})
.catch(error=>console.log(error)) //to catch the errors if any
}
componentDidMount(){
// this.fetchProducts();
this.props.fetchProduct().then(this.setState({loading : false}));
}
renderItem = ({ item }) => (
<ListItem
title={item.name}
subtitle={item.name}
leftAvatar={{
source: item.avatar && { uri: item.avatar },
title: item.name[0]
}}
bottomDivider
chevron
/>
)
render () {
if(!this.state.loading)
{
if(this.props.shopsInfo.loading)
{
return (
<ActivityIndicator/>
)
}
else
{
return (
<FlatList
vertical
showsVerticalScrollIndicator={false}
data={this.props.shopsInfo.shops}
renderItem={this.renderItem}
/>
)
}
}
else
{
return (
<ActivityIndicator/>
)
}
}
}
Product.propTypes = {
fetchProduct: propTypes.func.isRequired
};
const mapStateToProps = (state) => {
return { shopsInfo: state };
}
function mapDispatchToProps (dispatch) {
return {
fetchProduct: () => dispatch(fetchProduct())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Product);
1. Not updating on data change from backend.
You have to call an api on regular interval to get updated data. Redux implementation doesn't mean it will fetch data from server whenever there is any change.
2. Even values are not storing into the app
If you are expecting redux will store data even if you will close/kill an application than it will not. You have persist data in-order to use it or store it in cache. Take a look at redux-persist
The problem is your passing wrong props in mapStateToProps function.
In reducer your updating the response value in shop props.
In order to get the updated value you need to pass shops property to get the value.
const mapStateToProps = (state) => {
const { shops: state };
return {shops};
}

React native show a strange behavior. Can someone explain?

I'am creating a simple application with authentication. To change a state using redux with react-native-navigation (v1). For example, index.js
...
import { Navigation, } from 'react-native-navigation';
import { Provider, } from 'react-redux';
import store from './src/store';
import registerScreens from './src/screens';
registerScreens(store, Provider);
class App {
constructor () {
this.auth = false;
store.subscribe(this.onStoreUpdate.bind(this));
this.start();
}
onStoreUpdate () {
const state = store.getState();
if (this.auth != state.auth) {
this.auth = state.auth;
this.start();
}
}
start () {
switch (this.auth) {
case false:
Navigation.startTabBasedApp({
tabs: [{
screen: 'navigation.AuthScreen',
}, {
screen: 'navigation.RegisterScreen',
},],
});
break;
case true:
Navigation.startSingleScreenApp({
screen: {
screen: 'navigation.MainScreen',
},
});
break;
}
}
}
const application = new App();
Store is listening an update and change an application layout if need.
AuthScreen show a simple ActivityIndicator, when server request is perform. For example, auth.js
...
import { bindActionCreators, } from 'redux';
import { connect, } from 'react-redux';
import * as actions from './../actions';
...
class AuthScreen extends Component {
constructor (props) {
super(props);
this.state = {
loading: false,
...
};
this.handlePressEnter = this.handlePressEnter.bind(this);
}
handlePressEnter () {
...
this.loadingState(true);
jsonFetch(url, {
method: 'POST',
body: JSON.stringify({...}),
}).then((value) => {
this.loadingState();
this.props.actions.auth(true);
}).catch((errors) => {
this.loadingState();
console.log('Error while auth', errors);
});
}
...
loadingState (state = false) {
this.setState({
loading: state,
});
}
render () {
return (<View>
...
<Modal visible={this.state.loading} transparent={true} animationType="none" onRequestClose={() => {}}>
<View>
<ActivityIndicator size="large" animating={this.state.loading} />
</View>
</Modal>
</View>);
}
}
function mapStateToProps (state, ownProps) {
return {};
}
function mapDispatchToProps (dispatch) {
return {
actions: bindActionCreators(actions, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps) (AuthScreen);
I'am starting application with iOS simulator and try to authenticate. It show me activity indicator, then indicator disappear, but layout does not change. And strange behavior, if I comment this.loadingState(true); and this.loadingState(); in auth.js layout changes with success.
Can someone explain to me, why layout does not change from auth to main when activity indicator using?
I think that you can use dispatch props for loading.
For example When you call this.props.actions.auth(true);
You can return loading reducers.
handlePressEnter () {
...
dispatch({ type:'loading', loading: true });
jsonFetch(url, {
method: 'POST',
body: JSON.stringify({...}),
}).then((value) => {
dispatch({ type:'loading', loading: false });
this.props.actions.auth(true);
}).catch((errors) => {
this.loadingState();
console.log('Error while auth', errors);
});
}
And than you can use
<ActivityIndicator size="large" animating={this.props.loading} />
But dont forget the reducers return

Component's prop doesn't update in React Native with Redux

I need some help with my app and Redux! (Currently, i hate it aha)
So, i have a notification page component which fetch some datas and i need to put the data length into my redux store to put badge on my icon in my tabbar!
My Main Reducer :
import { combineReducers } from "redux";
import NotificationReducer from "./NotificationReducer";
export default function getRootReducer(navReducer) {
return combineReducers({
nav: navReducer,
notificationReducer: NotificationReducer
});
}
My Notification reducer
const initialState = {
NotificationCount: 0
};
export default function notifications(state = initialState, action = {}) {
switch (action.type) {
case 'SET_COUNT' :
console.log('REDUCER NOTIFICATION SET_COUNT',state)
return {
...state,
NotificationCount: action.payload
};
default:
return state;
}
};
My Action :
export function setNotificationCount(count) {
return function (dispatch, getState) {
console.log('Action - setNotificationCount: '+count)
dispatch( {
type: 'SET_COUNT',
payload: count,
});
};
};
My Component :
import React, { Component } from 'react';
import { View, Text, StyleSheet, ScrollView, Dimensions, TouchableOpacity, SectionList, Alert } from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import { Notification } from '#Components';
import { ORANGE } from '#Theme/colors';
import { NotificationService } from '#Services';
import Style from './style';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '#Redux/Actions';
const width = Dimensions.get('window').width
const height = Dimensions.get('window').height
export class NotificationsClass extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: [],
NotificationCount: undefined
};
}
async componentWillMount() {
this.updateNotifications();
}
componentWillReceiveProps(nextProps){
console.log('receive new props',nextProps);
}
async updateNotifications() {
this.props.setNotificationCount(10); <---
let data = await NotificationService.get();
if (data && data.data.length > 0) {
this.setState({ dataSource: data });
console.log(this.props) <-- NotificationCount is undefined
}
}
render() {
if (this.state.dataSource.length > 0) {
return (
<SectionList
stickySectionHeadersEnabled
refreshing
keyExtractor={(item, index) => item.notificationId}
style={Style.container}
sections={this.state.dataSource}
renderItem={({ item }) => this.renderRow(item)}
renderSectionHeader={({ section }) => this.renderSection(section)}
/>
);
} else {
return this.renderEmpty();
}
}
renderRow(data) {
return (
<TouchableOpacity activeOpacity={0.8} key={data.notificationId}>
<Notification data={data} />
</TouchableOpacity>
);
}
}
const Notifications = connect(
state => ({
NotificationCount: state.NotificationCount
}),
dispatch => bindActionCreators(Actions, dispatch)
)(NotificationsClass);
export { Notifications };
(I've removed some useless code)
Top Level :
const navReducer = (state, action) => {
const newState = AppNavigator.router.getStateForAction(action, state);
return newState || state;
};
#connect(state => ({
nav: state.nav
}))
class AppWithNavigationState extends Component {
render() {
return (
<AppNavigator
navigation={addNavigationHelpers({
dispatch: this.props.dispatch,
state: this.props.nav,
})}
/>
);
}
}
const store = getStore(navReducer);
export default function NCAP() {
return (
<Provider store={store}>
<AppWithNavigationState />
</Provider>
);
}
React : 15.6.1
React-Native : 0.46.4
Redux : 3.7.2
React-Redux : 5.0.5
React-Navigation : 1.0.0-beta.11
Node : 6.9.1
So if you've an idea! It will be great :D !
Thanks !
There's three issues.
First, React's re-rendering is almost always asynchronous. In updateNotifications(), you are calling this.props.setNotificationCount(10), but attempting to view/use the props later in that function. Even with the await in there, there's no guarantee that this.props.NotificationCount will have been updated yet.
Second, based on your reducer structure and mapState function, props.NotificationCount will actually never exist. In your getRootReducer() function, you have:
return combineReducers({
nav: navReducer,
notificationReducer: NotificationReducer
});
That means your root state will be state.nav and state.notificationReducer. But, in your mapState function, you have:
state => ({
NotificationCount: state.NotificationCount
}),
state.NotificationCount will never exist, because you didn't use that key name when you called combineReducers.
Third, your notificationReducer actually has a nested value. It's returning {NotificationCount : 0}.
So, the value you actually want is really at state.notificationReducer.NotificationCount. That means your mapState function should actually be:
state => ({
NotificationCount: state.notificationReducer.NotificationCount
}),
If your notificationReducer isn't actually going to store any other values, I'd suggest simplifying it so that it's just storing the number, not the number inside of an object. I'd also suggest removing the word Reducer from your state slice name. That way, you could reference state.notification instead.
For more info, see the Structuring Reducers - Using combineReducers section of the Redux docs, which goes into more detail on how using combineReducers defines your state shape.