i'm developing an android application whereby my screen has to depends on whether the user is logged in or not. The login data is stored inside the AsyncStorage.
By the time when the apps start, it should get the data from AsyncStorage and make it as the default state. How can i achieve that ?
Below is my redux structure
index.android.js
import {
AppRegistry,
} from 'react-native';
import Root from './src/scripts/Root.js';
AppRegistry.registerComponent('reduxReactNavigation', () => Root);
Root.js
import React, { Component } from "react";
import { Text, AsyncStorage } from "react-native";
import { Provider, connect } from "react-redux";
import { addNavigationHelpers } from 'react-navigation';
import getStore from "./store";
import { AppNavigator } from './routers';
const navReducer = (state, action) => {
const newState = AppNavigator.router.getStateForAction(action, state);
return newState || state;
};
const mapStateToProps = (state) => ({
nav: state.nav
});
const user = {};
class App extends Component {
constructor(){
super();
}
render() {
return (
<AppNavigator
navigation={addNavigationHelpers({
dispatch: this.props.dispatch,
state: this.props.nav
})}
/>
);
}
}
const AppWithNavigationState = connect(mapStateToProps)(App);
const store = getStore(navReducer);
export default function Root() {
return (
<Provider store={store}>
<AppWithNavigationState />
</Provider>
);
}
user reducers
import {
REGISTER_USER,
UPDATE_USER,
LOGIN_USER
} from '../../actions/actionTypes';
import { handleActions } from 'redux-actions';
**// here is the default state, i would like to get from asyncstorage**
const defaultState = {
isLoggedIn: false,
user:{},
};
export const user = handleActions({
REGISTER_USER: {
next(state, action){
return { ...state, user: action.payload, isLoggedIn: true }
}
},
LOGIN_USER: {
next(state, action){
return { ...state, user: action.payload, isLoggedIn: true }
}
},
}, defaultState);
You can use the component lifecycle method componentWillMount to fetch the data from AsyncStorage and when the data arrive you can change the state.
Related
I have Problem in dispatching an action and is suppose to update my state in redux.
This is my Homepage component. I am able to get the current state of the calculator balance.
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { Text, ScrollView, Button } from "react-native";
import * as reducerActions from "../../store/actions/calculator";
function Homepage() {
const balance = useSelector((state) => state.calculator.balance);
const dispatch = useDispatch();
console.log(balance);
return (
<ScrollView>
<Text>Balance:{balance}</Text>
<Button
title="Add to Cart"
onPress={() => {
dispatch(reducerActions.DepositMoney(10));
}}
/>
</ScrollView>
);
}
export default Homepage;
This is my action component: The issue is that it doesn't call my reducer. It logs the value of 10 when I press on the button.
export const DEPOSIT = "DEPOSIT";
export const DepositMoney = (amount) => {
return { type: DEPOSIT, payload: amount };
};
This is my reducer component:
import { DEPOSIT } from "../actions/calculator";
const initialState = {
balance: 0,
};
export default (state = initialState, action) => {
switch (action.Type) {
case DEPOSIT:
console.log("reducer");
console.log(action.payload);
return { balance: state.balance + action.payload };
case "WITHDRAW":
return { balance: state.balance - action.payload };
}
return state;
};
And this is how i set up the redux in my app.js
import React, { useState } from "react";
import { createStore, combineReducers, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import { AppLoading } from "expo";
import * as Font from "expo-font";
import ReduxThunk from "redux-thunk";
import productsReducer from "./store/reducers/products";
import ShopNavigator from "./navigation/ShopNavigator";
import calculatorReducer from "./store/reducers/calculator";
import { composeWithDevTools } from "redux-devtools-extension";
const rootReducer = combineReducers({
products: productsReducer,
calculator: calculatorReducer,
});
//composewithdevtools should be taken out for production
//const store = createStore(rootReducer, composeWithDevTools());
const store = createStore(
rootReducer,
composeWithDevTools(),
applyMiddleware(ReduxThunk)
);
const fetchFonts = () => {
return Font.loadAsync({
"open-sans": require("./assets/fonts/OpenSans-Regular.ttf"),
"open-sans-bold": require("./assets/fonts/OpenSans-Bold.ttf"),
});
};
export default function App() {
const [fontLoaded, setFontLoaded] = useState(false);
if (!fontLoaded) {
return (
<AppLoading
startAsync={fetchFonts}
onFinish={() => {
setFontLoaded(true);
}}
/>
);
}
return (
<Provider store={store}>
<ShopNavigator />
</Provider>
);
}
#Christian is right but one thing is missing.
Must copy state before making change in it.
You can also do it like.
export default (state = initialState, {type,payload}) => {
switch (type) {
case DEPOSIT:
console.log("reducer");
console.log(payload);
return { ...state, balance: state.balance + payload };
case "WITHDRAW":
return {...state, balance: state.balance - payload };
}
return state;
};
It was something silly. The following line was wrong:
switch (action.Type) {
It should be:
switch (action.type) {
I have combined my react redux.
Here is my App.js
import React from 'react';
import ReduxThunk from 'redux-thunk';
import { Provider } from 'react-redux';
import { compose, createStore, applyMiddleware } from 'redux';
import reducers from './src/reducers';
import AppContainer from './src/navigator'
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const App: () => React$Node = () => {
const store = createStore(reducers, {}, composeEnhancers(applyMiddleware(ReduxThunk)));
return (
<Provider store={store}>
<AppContainer />
</Provider>
);
};
export default App;
src/reducers/index.js
import { combineReducers } from 'redux';
import LoginReducer from './LoginReducer';
export default combineReducers({
LoginRedux: LoginReducer
});
If I use my action login(), I can see login action start, but I can't see dispatch start
import React from 'react';
import {
Text,
View,
TouchableOpacity,
} from 'react-native';
import { connect } from 'react-redux';
import { login } from '../actions';
const LoginScreen = ({ navigation }) => {
// console.log('see my test value', testValue)
return (
<View>
<TouchableOpacity
onPress={() => {
login();
}
}>
<View>
<Text>LOGIN</Text>
</View>
</TouchableOpacity>
</View>
</View>
);
}
const mapStateToProps = (state) => {
const { testValue } = state.LoginRedux;
console.log('mapStateToProps testValue =>', testValue);
return { testValue };
};
export default connect(mapStateToProps, { login })(LoginScreen);
If I console.log(dispatch), it will show dispatch is not defined.
import { LOGIN } from './types';
export const login = () => {
console.log('login action start')
return (dispatch) => {
console.log('dispatch start');
// console.log(dispatch);
dispatch({ type: LOGIN, testValue: 'I am test' });
};
};
src/reducers/LoginReducer.js
import { LOGIN } from '../actions/types';
const INITIAL_STATE = {
testValue: ''
};
export default (state = INITIAL_STATE, action) => {
console.log('reducer =>', action); // I can't see the console.log
switch (action.type) {
case LOGIN:
return {
...state,
testValue: action.testValue
};
default:
return state;
}
};
I have no idea why my action dispatch is not working. Do I set something wrong ?
Any help would be appreciated.
According to Zaki Obeid help, I update like this:
the action code:
export const login = () => {
console.log('login !');
return { type: LOGIN };
};
the function component code:
import { login } from '../../actions';
export const SettingScreen = ({ navigation, login }) => {
// return view code
}
const mapDispatchToProps = dispatch => ({
// you will use this to pass it to the props of your component
login: () => dispatch(login),
});
connect(null, mapDispatchToProps)(SettingScreen);
In LoginScreen component
you will need to add mapDispatchToProps
const mapDispatchToProps = dispatch => ({
// you will use this to pass it to the props of your component
login: () => dispatch(login()),
});
export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen);
Then
you will need to destructure from the props as:
const LoginScreen = ({ navigation, login }) => {
// your code
}
In actions.js
the way you use dispatch here requires a library redux-thunk and it's used for async calls.
and the normal action should do the job for you:
export const login = () => ({
type: LOGIN,
testValue: 'I am test'
})
I hope this is useful and will solve your problem,
Have a good day.
In a react-redux app, you obtain the dispatch function either from getting a hold of the store object directly (store.dispatch), or via the react-redux connect function, which will provide dispatch as an argument to a function you write and then later hook up to a component
import { connect } from 'react-redux';
const mapStateToProps = ...
const mapDispatchToProps = (dispatch) => {
return {
someHandle: () => dispatch(myActionCreator())
}
}
export const connect(mapStateToProps, mapDispatchToProps)(MyComponent)
You can't just call dispatch out of thin air -- it's not a global function.
It seems you are using the login function directly. you will have to use the props. Just change the name for confusing and use through props.
import { combineReducers } from 'redux';
import LoginReducer from './LoginReducer';
export default combineReducers({
LoginRedux: LoginReducer
});
If I use my action login(), I can see login action start, but I can't see dispatch start
import React from 'react';
import {
Text,
View,
TouchableOpacity,
} from 'react-native';
import { connect } from 'react-redux';
import { login } from '../actions';
const LoginScreen = ({ navigation, userLogin }) => {
// console.log('see my test value', testValue)
return (
<View>
<TouchableOpacity
onPress={() => {
userLogin();
}
}>
<View>
<Text>LOGIN</Text>
</View>
</TouchableOpacity>
</View>
</View>
);
}
const mapStateToProps = (state) => {
const { testValue } = state.LoginRedux;
console.log('mapStateToProps testValue =>', testValue);
return { testValue };
};
export default connect(mapStateToProps, { userLogin:login })(LoginScreen);
I created a sample React Native app to see if I can get Redux-persist working. However my React Native app with Redux Persist is not saving state to persisted storage.
Every time i change the toggle to 'true' and then reload the app, the state is not persisted and goes back to null.
How can I get 'True' to stay persisted when I refresh the app.
Here is my code:
index.js:
import {AppRegistry} from 'react-native';
import {name as appName} from './app.json';
import React, {Component} from 'react';
import { Provider } from "react-redux";
import { store, persistor } from "./Store/index";
import { PersistGate } from 'redux-persist/integration/react'
import App from './App.js';
class ReduxPersistTest extends Component {
render() {
return (
<Provider store={store}>
<PersistGate persistor={persistor} loading={null}>
<App />
</PersistGate>
</Provider>
);
}
}
AppRegistry.registerComponent('ReduxPersistTest', () => ReduxPersistTest);
store/index.js:
import { createStore, applyMiddleware, compose } from "redux";
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import toggle from "../Reducers/rootReducer.js";
import { AsyncStorage } from "react-native";
import {persistStore, persistReducer, persistCombineReducers} from "redux-persist";
import storage from 'redux-persist/lib/storage'
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
const togglePersistConfig = {
key: 'toggle',
storage: AsyncStorage
};
const middleware = [thunk];
const persistConfig = {
key: 'root',
storage: AsyncStorage,
debug: true,
whitelist: ['toggle']
}
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const reducers = { toggle: persistReducer(togglePersistConfig, toggle) };
const persistedReducer = persistCombineReducers(persistConfig, reducers);
export const store = createStore(
persistedReducer, composeEnhancers(applyMiddleware(...middleware))
);
export const persistor = persistStore(store);
Reducer.js
The issue might be found here in my reducer...
import { ADD_TOGGLE } from "../Constants/action-types";
import { combineReducers } from 'redux';
const initialState = {
toggle: false,
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TOGGLE:
console.log(action.payload.toggle);
console.log(action.payload);
return {
...state, toggle: {
toggle: action.payload.toggle,
}};
default:
return state;
}
};
export default rootReducer;
Actions/index.js
import { ADD_TOGGLE } from "../Constants/action-types";
export const addToggle = toggle => ({ type: ADD_TOGGLE, payload: toggle });
Constants/action-Types.js
export const ADD_TOGGLE = "ADD_TOGGLE";
And my components:
App.js
import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Copy from './Components/Copy/Copy.js';
import CopyToggle from './Components/Copy/CopyToggle.js';
/*
Redux imports
*/
import { connect } from "react-redux";
import { addToggle } from "./Actions/index";
/*
Redux constants
*/
const mapDispatchToProps = dispatch => {
return {
addToggle: toggle => dispatch(addToggle(toggle))
};
};
//Styles
const styles = StyleSheet.create({
textHeader: {
textAlign: 'center',
marginBottom: 10,
marginTop: 100,
},
});
class App extends Component {
constructor(props) {
super(props);
this.state = {
toggle: false,
};
}
componentWillMount() {
const { toggle } = this.state;
this.props.addToggle({ toggle });
}
render() {
return (
<View>
<Text style={styles.textHeader}>Welcome to React Native!</Text>
<Copy />
<CopyToggle />
</View>
)
}
}
export default connect(null, mapDispatchToProps)(App);
Copy.JS (toggle UI to change the toggle value from 'true' to 'false'
import React, { Component } from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';
import { compose } from 'react-compose';
import { Switch } from 'react-native-switch';
import { connect } from "react-redux";
import { addToggle } from "../../Actions/index";
const mapDispatchToProps = dispatch => {
console.log('mapDispatchToProps hit');
return {
addToggle: toggle => dispatch(addToggle(toggle))
};
};
class Copy extends Component {
constructor(props) {
super(props);
this.state = {
toggle: false,
};
this.addtoggle = this.addtoggle.bind(this);
}
addtoggle(val) {
this.setState({
toggle: val,
}, function () {
const { toggle } = this.state;
this.props.addToggle({ toggle });
});
}
render() {
return (
<View>
<Text>Test redux persist</Text>
<Switch
value={ this.state.toggle }
onValueChange={(val) => this.addtoggle(val)}
/>
</View>
);
}
}
export default connect(null, mapDispatchToProps)(Copy);
CopyToggle.js (outputs the boolean value of the toggle)
import React, { Component } from 'react';
/*
Redux imports
*/
import { connect } from "react-redux";
import { addToggle } from "../../Actions/index";
/*
Native base and react native
*/
import { StyleSheet, View, Text } from 'react-native';
/*
Redux constants
*/
const mapDispatchToProps = dispatch => {
return {
addToggle: toggle => dispatch(addToggle(toggle))
};
};
const mapStateToProps = state => {
return { toggle: state.toggle.toggle };
};
// Custom Styles
const styles = StyleSheet.create({
textHeader: {
color: '#000',
},
});
//class
class CopyToggle extends Component {
constructor(props) {
super(props);
this.state = {
purchase: false,
};
this.toggleDisplay = this.toggleDisplay.bind(this);
}
componentWillMount() {
const { toggle } = this.state;
this.props.addToggle({ toggle });
}
//display to output bollean value
toggleDisplay() {
let toggleState;
if (this.props.toggle === false) {
toggleState = 'false'
}
else if (this.props.toggle === true) {
toggleState = 'true'
}
return (
<Text>{toggleState}</Text>
)
}
//render
render() {
return (
<View>
{this.toggleDisplay()}
</View>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CopyToggle);
It would be greatly appreciated id someone with knowledge on Redux persist can review and hopefully point out my issue.
THanks!
You need to use persistCombineReducers if you want to persist just part of your store, so I would come up with something similar to this:
import { persistCombineReducers, persistReducer } from 'redux-persist';
import toggle from './Reducer.js';
const togglePersistConfig = {
key: 'toggle',
storage: AsyncStorage
};
const reducers = {
toggle: persistReducer(togglePersistConfig, toggle),
// ...other reducers
}
const persistConfig = {
key: 'root',
storage: AsyncStorage,
debug: true,
whitelist: ['toggle']
};
const persistedReducer = persistCombineReducers(persistConfig, reducers);
export const store = createStore( persistedReducer, composeEnhancers(applyMiddleware(...middleware)) );
You have to add the value you want to persist in the storage object:
AsyncStorage.setItem('toggle': this.state.toggle)
index.js
import React from 'react';
import {
AppRegistry
} from 'react-native'
import App from './App';
import { YellowBox } from 'react-native';
YellowBox.ignoreWarnings(['Warning: isMounted(...) is deprecated', 'Module RCTImageLoader']);
AppRegistry.registerComponent('mhagora', () => App);
App.js
import React, { Component } from 'react';
import { Provider } from "react-redux";
import store from './app/store';
import { StyleProvider, getTheme } from "native-base";
import Setup from "./app/setup";
import variables from "./app/theme/variables/commonColor";
export default class App extends Component {
render() {
return (
<Provider store={store}>
<StyleProvider style={getTheme(variables)}>
<Setup />
</StyleProvider>
</Provider>
);
}
}
./app/setup.js
import React, { Component } from "react";
import axios from "axios/index";
import Config from "./config";
import { Root } from "native-base";
import AppNavigator from "./routes";
axios.defaults.baseURL = Config.API_BASE_URL;
axios.defaults.headers.common['Content-Type'] = Config.API_ACCEPT;
axios.defaults.headers.common['Accept'] = Config.API_ACCEPT;
axios.defaults.headers.common['secret'] = Config.API_SECRET;
export default class Setup extends Component {
render() {
return (
<Root>
<AppNavigator />
</Root>
);
}
}
./app/store/index.js
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { createLogger } from 'redux-logger';
import reducers from '../reducers';
const logger = createLogger();
export default createStore(reducers, compose(applyMiddleware(thunk, logger)));
./app/actions/index.js
import { APP_LOADING, APP_LOADED } from '../actionTypes';
export function appLoading() {
return (dispatch) => {
dispatch({type: APP_LOADING});
}
}
export function appLoaded() {
return (dispatch) => {
dispatch({type: APP_LOADED});
}
}
./app/actions/user.js
import { USER_LOADING, USER_LOADED, USER_FAILED, APP_LOADING, APP_LOADED } from "../actionTypes";
import axios from 'axios';
import Config from '../config';
export function userLogin(username, password) {
return (dispatch) => {
dispatch({type: USER_LOADING});
axios
.post("oauth/token", {
username: username,
password: password,
client_id: Config.API_CLIENT_ID,
client_secret: Config.API_CLIENT_SECRET,
grant_type: 'password',
}, {
headers: {}
})
.then(response => {
dispatch({
type: USER_LOADED,
data: response.data
});
})
.catch(err => {
dispatch({ type: USER_FAILED, error: err.response.data.message });
alert(err.response.data.message);
});
};
}
./app/reducers/index.js
import appReducer from './appReducer';
import userReducer from './userReducer';
import { combineReducers } from "redux";
const rootReducer = combineReducers({
appReducer,
userReducer
});
export default rootReducer;
./app/reducers/userReducer.js
import { USER_LOADING, USER_LOADED, USER_FAILED } from '../actionTypes';
const initialState = {
username: "",
password: "",
user: {}
};
export default userReducer = (state = initialState, action) => {
switch (action.type) {
case USER_LOADING:
return Object.assign({}, state, {
loading: true,
user: {},
});
case USER_LOADED:
return Object.assign({}, state, {
loading: false,
user: action.data
});
case USER_FAILED:
return Object.assign({}, state, {
loading: false,
});
default:
return state
}
}
./app/reducers/appReducer.js
import { APP_LOADING, APP_LOADED } from "../actionTypes";
const initialState = {
loading: true,
};
export default appReducer = (state = initialState, action) => {
switch (action.type) {
case APP_LOADING:
return Object.assign({}, state, {
loading: true
});
case APP_LOADED:
return Object.assign({}, state, {
loading: false
});
default:
return state;
}
};
./app/screens/home.js
'use strict';
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { SkypeIndicator } from 'react-native-indicators';
import * as Actions from '../actions/index';
import { Container, Header, Title, Content, Footer, FooterTab, Button, Left, Right, Body, Icon, Text, View } from 'native-base';
class HomeScreen extends Component {
componentDidMount() {
/** HERE, the apps should show a loading page forever but it didn't **/
// setTimeout( _ => {
// this.props.appLoaded();
// }, 2000);
}
render() {
if (this.props.loading) {
return (
<SkypeIndicator />
);
} else {
return (
<Container>
<Header>
</Header>
<Body>
<Button
onPress={() =>
this.props.navigation.navigate('LoginScreen')
}><Text>Login now</Text></Button>
<Text>Hello</Text>
</Body>
</Container>
);
}
}
}
// The function takes data from the app current state,
// and insert/links it into the props of our component.
// This function makes Redux know that this component needs to be passed a piece of the state
function mapStateToProps(state, props) {
return {
loading: state.loading,
user: state.user,
}
}
// Doing this merges our actions into the component’s props,
// while wrapping them in dispatch() so that they immediately dispatch an Action.
// Just by doing this, we will have access to the actions defined in out actions file (action/homeScreen.js)
function mapDispatchToProps(dispatch) {
return bindActionCreators(Actions, dispatch);
}
//Connect everything
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen);
./app/screens/loginScreen
'use strict';
import React, { Component } from 'react';
import { StyleSheet } from 'react-native';
import { SkypeIndicator } from 'react-native-indicators';
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import { Body, Button, Container, Content, Header, Icon, Left, Text, Title, View } from "native-base";
import t from 'tcomb-form-native';
import { LoginUserModel, LoginUserModelOption } from "../models/UserModel";
import styles from '../styles';
import LoadingButton from 'react-native-loading-button';
import * as UserActions from '../actions/user';
const Form = t.form.Form;
const ps = StyleSheet.create({
...styles,
container: {
justifyContent: 'center',
marginTop: 50,
padding: 20
},
});
class LoginScreen extends Component {
constructor(props) {
super(props);
}
onSubmitHandler = () => {
const value = this._form.getValue();
if(value) {
this.props.userLogin(value.username, value.password);
}
};
render() {
return (
<Container>
<Header>
<Left>
<Button transparent onPress={() => this.props.navigation.goBack()}>
<Icon name="arrow-back"/>
</Button>
</Left>
<Body>
<Title>Headers</Title>
</Body>
</Header>
<Content padder>
<View style={ps.container}>
<Form ref={c => this._form = c} type={LoginUserModel} options={LoginUserModelOption} />
<LoadingButton
block
onPress={this.onSubmitHandler.bind(this)}
isLoading={this.props.loading}
style={{ justifyContent: 'center' }}
><Icon name="checkmark"/><Text>Login Now</Text></LoadingButton>
</View>
</Content>
</Container>
);
}
}
// The function takes data from the app current state,
// and insert/links it into the props of our component.
// This function makes Redux know that this component needs to be passed a piece of the state
function mapStateToProps(state, props) {
return {
loading: state.loading,
user: state.user,
}
}
// Doing this merges our actions into the component’s props,
// while wrapping them in dispatch() so that they immediately dispatch an Action.
// Just by doing this, we will have access to the actions defined in out actions file (action/homeScreen.js)
function mapDispatchToProps(dispatch) {
return bindActionCreators(UserActions, dispatch);
}
//Connect everything
export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen);
the homeScreen should result in a forever loading page but it didn't
the loginScreen button should automatically loading when pressing, but it didn't
new to react-native, i have tried to set/play with the state/props but it just seems like not changing/connected, i also have another page trying to check the state is synced but results is like always get the fresh state, as my understanding the state is something like GLOBAL variable accessible in any component connect to redux
MY QUESTION IS
1. is the react-native/redux/redux-thunk setup correctly? if not, where is the error
2. is the state/props is global accessible in any component that connect with redux
3. if statement 2 is correct, what the different between state/props? this.state and this.props
4. i don't really understand the promise work, how can we handle / wait untill the api call complete(success/error) before move to next step/flow, i use php a lot and my logic is stuck at each function should return something then depends on the results process to next function...and then...
your answer / precious time spend for reading this question is appreciated, thank you
created a github for easy to reproduce/test
https://github.com/weiloon1234/react-native-test
I'm trying to get data from App.js for network connection availability. I'm getting data from App.js to action and reducer but the reducer is not updating the state for my component. The console log in the reducer is working but I'm not able to get data in the mapStateToProps of myComponent.
My App.js file contains this code.
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { NetInfo } from 'react-native';
import { createStore, applyMiddleware } from 'redux';
import ReduxThunk from 'redux-thunk';
import reducers from './src/reducers';
import Router from './src/Router';
import { internetConnectionChanged } from './src/actions/';
class App extends Component {
componentWillMount() {
NetInfo.isConnected.addEventListener('connectionChange', this.handleConnectionChange);
}
componentWillUnmount() {
NetInfo.isConnected.removeEventListener('connectionChange', this.handleConnectionChange);
}
handleConnectionChange = (isConnected) => {
NetInfo.isConnected.fetch().done(
(isConnecteds) => {
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));
store.dispatch(internetConnectionChanged(isConnecteds));
});
};
render() {
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));
return (
<Provider store={store}>
<Router />
</Provider>
);
}
}
export default App;
My code in action file is
import { CONNECTION_CHANGE } from '../actions/types';
export const internetConnectionChanged = (isConnected) => {
return {
type: CONNECTION_CHANGE,
payload: isConnected
};
};
That is exported via the index.js of actions file
through export * from './AppActions';
Code for the reducer is
import { CONNECTION_CHANGE } from '../actions/types';
const INITIAL_STATE = { isConnected: false };
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case CONNECTION_CHANGE:
console.log(action.payload);
return { ...state, isConnected: action.payload };
default:
return state;
}
};
Under my component, this is the code to get the info is
const mapStateToProps = ({ auth, app }) => {
const { email, password, error, loading } = auth;
const { isConnected } = app;
return { email, password, error, loading, isConnected };
};
export default connect(mapStateToProps, {
emailChanged,
passwordChanged,
loginUser,
forgotPasswordAction,
})(LoginForm);
Create store outside the App class. This might be causing the store to always have initial reducer values. Just paste the below line before Class App extends Component line
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));
Also remove the same above line of code from the following function
handleConnectionChange = (isConnected) => {
NetInfo.isConnected.fetch().done(
(isConnecteds) => {
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk)); //remove this line
store.dispatch(internetConnectionChanged(isConnecteds));
});
};