how to use Redux with createSwitchNavigator? - react-native

I am trying to use switch navigator with redux. following is my code.
import { createSwitchNavigator, createStackNavigator } from 'react-navigation';
import LoginScreen from '../screens/LoginScreen';
import EmployeeListScreen from '../screens/EmployeeListScreen';
import DetailsView from '../screens/EmployeeDetailViewScreen';
import EmployeeForm from '../screens/EmployeeForm';
import AuthLoadingScreen from "../screens/AuthLoadingScreen.js";
import {connect } from "react-redux";
import { AppNavigator } from "../navigations/AppNavigator.js";
const AppStack = createStackNavigator({
List:{screen:EmployeeListScreen},
Detail:{screen:DetailsView},
Form:{screen:EmployeeForm}
});
const AuthStack = createStackNavigator({ Login: LoginScreen });
export const AuthNavigator = createSwitchNavigator(
{
AuthLoading: AuthLoadingScreen,
App: AppNavigator,
Auth: AuthStack,
},
{
initialRouteName: 'AuthLoading',
}
);
const AuthWithNavigationState = ({ dispatch, nav }) => (
<AuthNavigator />
);
const mapStateToProps = state => ({
nav: state.nav,
});
export default connect(mapStateToProps)(AuthWithNavigationState);
I'm importing this component is my App.js file and using it as follows to connect with redux store but it is giving me error like, React is not defined and error is located at connect(AuthWithNavigationState)
import React from 'react';
import { Provider } from 'react-redux';
import { createStore,applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import AppReducer from './src/reducers/AppReducer';
import AuthWithNavigationState from './src/navigations/AuthNavigator.js';
const store = createStore(AppReducer,applyMiddleware(thunk));
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
<AuthWithNavigationState/>
</Provider>
);
}
}
I've tried to follow instruction from below link but still I am getting the same error.
how can i integrate redux store to react native?
Can anyone tell me what is wrong with my code?

after too much trying, I figure out that, The switch navigator don't need to be connect to the store, so below step was incorrect.
const AuthWithNavigationState = ({ dispatch, nav }) => (
<AuthNavigator />
);
const mapStateToProps = state => ({
nav: state.nav,
});
export default connect(mapStateToProps)(AuthWithNavigationState);
I simply exported AuthNavigator and imported in App.js and this worked for me.
import React from 'react';
import { Provider } from 'react-redux';
import { createStore,applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import AppReducer from './src/reducers/AppReducer';
import AuthNavigatorfrom './src/navigations/AuthNavigator.js';
const store = createStore(AppReducer,applyMiddleware(thunk));
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
<AuthNavigator/>
</Provider>
);
}
}

Related

How can I wrap my React Navigator in a redux store?

In my app.js file, I have a Navigator and I am attempting to wrap it in a redux store in order to control the state throughout my app. I am receiving this error though:
"Invariant Violation: The navigation prop is missing for this navigator. In react-navigation v3 and v4 you must set up your app container directly."
How can I properly wrap my navigator in the Redux store so I can use it?
App.js
import React from 'react';
import { registerRootComponent } from 'expo';
import { View, Text, Button } from 'react-native';
import { createAppContainer, createStackNavigator } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import thunkMiddleware from 'redux-thunk'
import reducer from './store/reducers'
// Components
import Home from './Screens/Home';
import Landing from './Screens/Landing';
import Tasks from './Screens/Tasks';
import Login from './Screens/Login';
const middleware = applyMiddleware(thunkMiddleware)
const store = createStore(reducer, middleware)
const Navigator = createStackNavigator({
Tasks: {screen: Tasks},
Landing: {screen: Landing},
Home: { screen: Home },
});
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
<Navigator />
</Provider>
)
}
}
I figure out how to accomplish this. Below is the updated code with my App wrapped in a store container.
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import thunkMiddleware from 'redux-thunk'
import reducer from './store/reducers'
// Components
import Home from './Screens/Home';
import Landing from './Screens/Landing';
import Tasks from './Screens/Tasks';
import Login from './Screens/Login';
const middleware = applyMiddleware(thunkMiddleware)
const store = createStore(reducer, middleware)
const Navigator = createStackNavigator({
Tasks: {screen: Tasks},
Landing: {screen: Landing},
Home: { screen: Home },
});
//ADDED THIS
const App_1 = createAppContainer(Navigator);
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
<App_1 /> //UPDATED
</Provider>
)
}
}

Can't connect components with mapDispatchToProps

I have React Native project with Redux and I'm trying to connect the actions to the components.
I have App.js file without index.js file.
This is how I implement Redux:
App.js:
import React from 'react';
import { Platform, StatusBar, StyleSheet, View } from 'react-native';
import { Provider } from 'react-redux';
import store from './src/store/Store.js';
import AppNavigator from './src/navigation/AppNavigator';
export default function App(props) {
return (
<Provider store = { store }>
<View style={styles.container}>
{Platform.OS === 'ios' && <StatusBar barStyle="default" />}
<AppNavigator />
</View>
</Provider>
);
}
AppNavigator.js:
import React from 'react';
import { createAppContainer, createSwitchNavigator } from 'react-navigation';
import MainTabNavigator from './MainTabNavigator';
export default createAppContainer(
createSwitchNavigator({
Main: MainTabNavigator
})
);
MainTabNavigator.js: (Only the relevant part)
import React from 'react'
import {connect} from 'react-redux';
import { createStackNavigator, createBottomTabNavigator } from 'react-navigation';
import {HomeScreen} from '../screens/HomeScreen';
import * as CounterActions from '../store/actions/CounterActions';
let HomePage = connect(state => mapStateToProps)(HomeScreen);
const HomeStack = createStackNavigator(
{
Home: HomePage,
},
config
);
const tabNavigator = createBottomTabNavigator({
HomeStack,
SettingsStack,
});
const mapStateToProps = (state) => {
return {
count: state.counter.count
}
};
const mapDispatchToProps = {
...CounterActions
};
export default tabNavigator;
CounterActions.js:
export const increment = (number) => {
return (dispatch) => {
dispatch({ type: 'INCREMENT', number })
}
};
export const decrement = (number) => {
return (dispatch) => {
dispatch({ type: 'DECREMENT', number })
}
};
The following line in MainTabNavigator.js connects the state to props of the HomeScreen component:
let HomePage = connect(state => mapStateToProps)(HomeScreen);
HomeScreen.js:
import React from 'react';
import { Platform, StyleSheet, Text, View } from 'react-native';
export const HomeScreen = (props) => {
alert(JSON.stringify(props));
return (
<View style={styles.container}>
<Text>COUNT FROM STORE: {props.count}</Text>
</View>
);
};
HomeScreen components gets the state correctly and render 'count', but How do I connect the actions?
I want HomeScreen to dispatch like this:
props.increment(1);
Thanks!
The mapDispatchToProps is the second argument of the connect function from the react-redux.
I also think that you pass wrong the first argument to the connect.
Try this:
let HomePage = connect(mapStateToProps, mapDispatchToProps)(HomeScreen);
Finally solved it by the following way:
MainTabNavigator.js:
import React from 'react'
import {connect} from 'react-redux';
import { createStackNavigator, createBottomTabNavigator } from 'react-navigation';
import {HomeScreen} from '../screens/HomeScreen';
import {increment, decrement} from '../store/actions/CounterActions';
let HomePage = connect(state => mapStateToProps, dispatch => mapDispatchToProps(dispatch))(HomeScreen);
const HomeStack = createStackNavigator(
{
Home: HomePage,
},
config
);
const tabNavigator = createBottomTabNavigator({
HomeStack,
SettingsStack,
});
const mapStateToProps = (state) => {
return {
count: state.counter.count
}
};
const mapDispatchToProps = (dispatch) => {
return {
increment: (number) => dispatch(increment(number)),
decrement: (number) => dispatch(decrement(number))
}
};
export default tabNavigator;
Follow along, we will make some modifications to your files:
First lets modify your MainTabNavigator.js since you only posted the relevant part, make sure to implement this for the rest as well.
import React from 'react'
import { createStackNavigator, createBottomTabNavigator } from 'react-navigation';
import {HomeScreen} from '../screens/HomeScreen';
let HomePage = connect(state => mapStateToProps)(HomeScreen); // <===== Remove this
const HomeStack = createStackNavigator(
{
Home: HomePage, // <===== Make this HomeScreen instead of HomePage
},
config
);
const tabNavigator = createBottomTabNavigator({
HomeStack,
SettingsStack,
});
const mapStateToProps = (state) => {
return {
count: state.counter.count
}
};
const mapDispatchToProps = {
...CounterActions
};
export default tabNavigator;
What we want is to have the mapping of state and props on the Home Screen itself (or any other screen)
Now lets move on to your HomeScreen.js:
import React from 'react';
import { connect } from 'react-redux';
import { Platform, StyleSheet, Text, View } from 'react-native';
import { increment, decrement } from '../store/actions/CounterActions'; // <===== import your actions here, preferably like this
/** add the following: */
const mapStateToProps = (state, ownProps) => ({
// ... computed data from state and optionally ownProps
});
const mapDispatchToProps = {
// ... normally is an object full of action creators
increment, // <===== Map your dispatch here to props
decrement // <===== Mapping the second dispatch
};
const HomeScreen = (props) => {
alert(JSON.stringify(props));
return (
<View style={styles.container}>
<Text>COUNT FROM STORE: {props.count}</Text>
</View>
);
};
/** Export your component like this */
export default connect(
mapStateToProps,
mapDispatchToProps
)(HomeScreen)
Now anywhere on your HomeScreen.js you can call this.props.increment(yourNumber) or this.props.decrement(yourNumber) and you should be good to go
Hope this Helps!

Cannot access redux props inside react-navigation v3 tab navigator (getting error this.props.<function_name> is not a function)

I am having trouble connecting the tab navigator (from React-Navigation v3) to the redux provider.
So this is how my react-navigation and redux is configured:
app/
/react-redux provider
/app-container (this is the AppContainer of the main SwitchNavigator)
/welcome-screen
/login-screen
/register-screen
/home-navigator (this is the TabNavigator that is inside the SwitchNavigator)
Home navigator holds the screens to which user is directed to when he logs-in. In the first 3 screends (welcome, login and register) I can access the functions that are "connected" from redux to the screen through standard mapDispatchToProps.
This is the app component:
/**
* Entry point for the app
*/
import React, { Component } from "react";
// redux
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import reduxThunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import main_app_reducer from "./modules/core/reducers/main_app_reducer";
// create redux store
const create_store = composeWithDevTools(applyMiddleware(reduxThunk))(createStore);
export const store = create_store(main_app_reducer);
// navigation
import AppContainer from "./modules/core/components/AppNavigator";
import { configure_axios } from "./helpers/axios_config";
configure_axios();
class App extends Component {
render() {
return (
<Provider store={store}>
<AppContainer />
</Provider>
);
}
}
export default App;
And then, from there we go to AppContainer:
/**
* this components ensures app navigation so that user can jump from one screen to another
*/
// navigation
import { createSwitchNavigator, createAppContainer } from "react-navigation";
// screens
import WelcomeScreen from "../screens/WelcomeScreen";
import LoginScreen from "../../auth/screens/LoginScreen";
import RegisterScreen from "../../auth/screens/RegisterScreen";
import HomeNavigator from "./HomeNavigator";
const AppSwitchNavigator = createSwitchNavigator({
// for users that are not logged in yet
Welcome: { screen: WelcomeScreen },
Login: { screen: LoginScreen },
Register: { screen: RegisterScreen },
// for logged in users
HomeNavigator: { screen: HomeNavigator },
});
const AppContainer = createAppContainer(AppSwitchNavigator);
export default AppContainer;
All of the code above works perfectly with no errors, the problem occurs inside the HomeNavigator component that you see below.
This is the HomeNavigator component:
import { createBottomTabNavigator } from "react-navigation";
import { HomeScreen } from "../screens/HomeScreen";
import { DashboardScreen } from "../screens/DashboardScreen";
const HomeNavigator = createBottomTabNavigator(
{
Home: { screen: HomeScreen },
Dashboard: { screen: DashboardScreen },
},
{
navigationOptions: ({ navigation }) => {
const { routeName } = navigation.state.routes[navigation.state.index];
return {
headerTitle: routeName,
};
},
},
);
export default HomeNavigator;
NONE of the screens inside the HomeNavigator are connected to Redux altough they are defined THE SAME WAY as the screens inside AppContainer file. For comparison let's see Login screen from AppContainer and Home screen from HomeNavigator:
LoginScreen:
import React, { Component } from "react";
import { View, Text } from "react-native";
import { connect } from "react-redux";
import { get_user_servers } from "../actions/core_actions";
export class LoginScreen extends Component {
componentDidMount() {
this.props.get_user_servers(); // this function works absolutely fine
}
render() {
return (
<View>
<Text> LoginScreen </Text>
</View>
);
}
}
const mapStateToProps = (state) => ({});
const mapDispatchToProps = { get_user_servers };
export default connect(
mapStateToProps,
mapDispatchToProps,
)(LoginScreen);
HomeScreen:
import React, { Component } from "react";
import { View, Text } from "react-native";
import { connect } from "react-redux";
import { get_user_servers } from "../actions/core_actions";
export class HomeScreen extends Component {
componentDidMount() {
this.props.get_user_servers();
// the same function as in LoginScreen but here the function does not work
// I'm getting an error = TypeError: this.props.get_user_servers is not a function (see picture below)
}
render() {
return (
<View>
<Text> HomeScreen </Text>
</View>
);
}
}
const mapStateToProps = (state) => ({});
const mapDispatchToProps = { get_user_servers };
export default connect(
mapStateToProps,
mapDispatchToProps,
)(HomeScreen);
That function (get_user_servers) is defined properly and it works fine in LoginScreen.
thanks everyone who took their time to help me, in the end it was my mistake that I overlooked. If you check out the third code-block from this question you can see that I import my components like this:
import { HomeScreen } from "../screens/HomeScreen";
import { DashboardScreen } from "../screens/DashboardScreen";
Which is wrong, I changed that to:
import HomeScreen from "../screens/HomeScreen";
import DashboardScreen from "../screens/DashboardScreen";
And now everything works fine, thanks again :)
Assuming it's the same function you're importing, please could you try to check your import of get_user_servers function from both screen : LoginScreen and HomeScreen. According to your code it look like LoginScreen ("../../auth/screens/LoginScreen") and HomeScreen ("../screens/HomeScreen") are not in the same path.
As asked above, could check the path of the files, or post it so we can help. Also could you open the remote debugger, and inside the componentWillMount you can put:
console.log(this.props)
to check if the HomeScreen is geting the function inside props.

createReduxBoundAddListener is deprecated in react-navigation-redux-helpers#2.0.0! Please use reduxifyNavigator instead

I'm using react-navigation and redux for navigation between screens I'm getting an error instead.
Invariant Violation: createReduxBoundAddListener is deprecated in
react-navigation-redux-helpers#2.0.0! Please use reduxifyNavigator
I don't know how to fix it please help me out.
App.js
import React from 'react';
import { Provider } from "react-redux";
import store from "./src/redux/store"
import { createReduxBoundAddListener } from "react- navigation-redux-helpers";
import AppWithNavigationState from "./src/navigators/AppNavigator";
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
//Here Im getting an error regarding creacteReduxBoundAddListener
<AppWithNavigationState listener{createReduxBoundAddListener("root")}/>
</Provider>
);
}
}
AppNavigator.js file
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import {connect} from "react-redux";
import {PropTypes} from 'prop-types';
import {addNavigationHelpers,createStackNavigator } from "react-navigation";
import LoggedOut from "../screens/LoggedOut";
import LogIn from "../screens/Login";
import ForgotPassword from "../screens/ForgotPassword";
// created screens using createStackNavigator
export const AppNavigator = createStackNavigator({
LoggedOut:{screen:LoggedOut},
LogIn:{screen:LogIn},
ForgotPassword:{screen:ForgotPassword},
});
const AppWithNavigationState = ({ dispatch,nav,listener}) =>{
<AppNavigator navigation=
{addNavigationHelpers({dispatch,state:nav,addListener:listener})}/>
};
AppWithNavigationState.propTypes = {
dispatch:PropTypes.func.isRequired,
nav:PropTypes.object.isRequired
};
const mapStateToProps = state =>({
nav:state.nav,
});
export default connect(mapStateToProps)(AppWithNavigationState);
navigation.js
import {AppNavigator} from "../../navigators/AppNavigator";
const firstAction =
AppNavigator.router.getActionForPathAndParams("LoggedOut"); //decide which
//screen will
//load first
const initialNavState = AppNavigator.router.getStateForAction(firstAction);
export const nav = (state=initialNavState, action)=>{
let nextState = AppNavigator.router.getStateForAction(action,state);
return nextState || state;
}
You can check this repo https://github.com/JaiDeves/AirBnB-React, I've updated the code with the newer version of react-navigation.

React native initalRouteName not working with Stack Navigation

I am new to react-native and I am trying to implement a simple application using StackNavigation and react-redux with welcome and signup screens. I have configured both the screens using StackNavigation but for some reasons , only the SignUp screen pops up when the app starts. Below are my files :
Index.js
import { AppRegistry } from 'react-native';
import App from './App';
AppRegistry.registerComponent('MyApp', () => App);
App.js
import React, { Component } from 'react';
import { Provider, connect } from "react-redux";
import { addNavigationHelpers } from "react-navigation";
import StackNavConfig from "./js/config/routes";
import getStore from "./js/store";
const AppNavigator = StackNavConfig;
const initialState = AppNavigator.router.getActionForPathAndParams('Welcome');
const navReducer = (state = initialState, action) => {
const newState = AppNavigator.router.getStateForAction(action, state);
return newState || state;
};
const AppWithNavigationState = connect(state => ({
nav: state.nav,
}))(({ dispatch, nav }) => (
<AppNavigator navigation={addNavigationHelpers({ dispatch, state: nav })} />
));
const store = getStore(navReducer);
class App extends React.Component {
render() {
return (
<Provider store={store}>
<AppWithNavigationState />
</Provider>
);
}
}
export default App;
js/config/routes.js
import Welcome from "../components/Welcome/view/Welcome";
import SignUp from "../components/SignUp/view/SignUp";
import { StackNavigator } from "react-navigation";
const Routes = {
Welcome: { screen: Welcome , path: ''},
SignUp: { screen: SignUp , path : '/signup'},
};
const RoutesConfig = {
initialRouteName: 'Welcome',
headerMode: 'none',
};
export default StackNavConfig = StackNavigator(Routes, RoutesConfig);
store.js
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import getRootReducer from "./reducers/index";
export default function getStore(navReducer) {
const store = createStore(
getRootReducer(navReducer),
undefined,
applyMiddleware(thunk)
);
return store;
}
Below are my components
Welcome.js
import React from 'react';
import {
View,
Image} from 'react-native';
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import * as welcomeActions from "../actions/WelcomeActions";
import { welcomeStyles } from '../styles/WelcomeStyles';
class Welcome extends React.Component {
constructor(){
super();
this.state = { };
}
render(){
return (
<View style = {welcomeStyles.mainContainer}>
<Text>Welcome</Text>
</View>
);
}
}
export default connect(
state => ({
}),
dispatch => bindActionCreators(welcomeActions, dispatch)
)(Welcome);
SignUp.js
import React from 'react';
import {
View,
Image} from 'react-native';
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import * as welcomeActions from "../actions/SignUpActions";
import { signUpStyles } from '../styles/SignUpStyles';
class Welcome extends React.Component {
constructor(){
super();
this.state = { };
}
render(){
return (
<View style = {signUpStyles.mainContainer}>
<Text>SignUp</Text>
</View>
);
}
}
export default connect(
state => ({
}),
dispatch => bindActionCreators(signUpActions, dispatch)
)(SignUp);
I also have action and reducer files for each of my component.But they are blank as of now , since I haven't yet implemented the redux part.I am combining the reducers as below.
import { combineReducers } from "redux";
import welcomeReducer from "../components/Welcome/reducers/WelcomeReducer";
import signUpReducer from "../components/SignUp/reducers/SignUpReducer";
export default function getRootReducer(navReducer) {
return combineReducers({
nav: navReducer,
welcomeReducer : welcomeReducer,
signUpReducer : signUpReducer,
});
}
As mentioned before , even after setting the initialRouteName to Welcome in my routes.js , the SignUp screen appears first everytime I launch the app. Please help
I found out what was the issue. I was calling this.props.navigate inside the render function by mistake which was causing the navigation to different screen.