3
votes

I want to navigate from my reduc action file. 1st screen is login so after click on login button will call a redux action creator and reducer generate a new state. So, after the action i want to rediret to my dashboard.

I am using

react native + redux + react navigation

Login component file (LoginCmp.js):

import React, { Component } from 'react';
import {
  Text,
  View,
  ScrollView,
  KeyboardAvoidingView
} from 'react-native';
import { connect } from 'react-redux';
import { fieldChange, LoginAction } from '../../actions';
import { Button, Section, Input, Alert } from '../Helpers';

LoginAction(){
  const payload = {
    email: this.props.email,
    password: this.props.password
  };
  this.props.LoginAction(payload); // Action call
}


render() {
    return (
      <KeyboardAvoidingView
    style={styles.container}
    behavior="padding"
      >
      <View>
    <ScrollView contentContainerStyle={styles.contentContainer}>
      <Text style={styles.headerText}> Login </Text>
      {this.alertMessage()}
      <Section>
        <Input
          placeholder="[email protected]"
          onChangeText={this.onFieldChange.bind(this, { actionType: 'EMAIL_CHANGED' })}
          value={this.props.email}
        />
      </Section>
      <Section>
        <Input
          secureTextEntry
          placeholder="Password"
          onChangeText={this.onFieldChange.bind(this, { actionType: 'PASSWORD_CHANGED' })}
          value={this.props.password}
        />
      </Section>
      <Section>
        <Button
          onPress={this.LoginAction.bind(this)}
          style={styles.buttonLogin}
        >
          Submit
        </Button>
      </Section>
    </ScrollView>
      </View>
      </KeyboardAvoidingView>
    );
  }
}

const mapStateToProps = ({ auth }) => {
  console.log(auth);
  return auth;
};

export default connect(mapStateToProps, { fieldChange, LoginAction })(LoginCmp);

Router file (Router.js):

const RouterComponent = StackNavigator({
  login: { screen: LoginCmp },
  dashboard: { screen: DashboardCmp },
});

Action creator file (AuthActions.js):

import firebase from 'firebase';
import { NavigationActions } from 'react-navigation';


// Login actions
export const LoginAction = (payload) => {
  return (dispatch) => {
    firebase.auth().signInWithEmailAndPassword(payload.email, payload.password)
      .then(user => loginSuccess(dispatch, user))
      .catch((error) => {
    loginFail(dispatch, error);
    });
  };
};

// Login success function

logoutSuccess = (dispatch, user) => {
// dispatch an action
dispatch({
  type: 'LOGOUT_SUCCESS',
  payload: user
});  
### From here i want to navigate to dashboard.
};
6

6 Answers

9
votes

EDIT: 9 Aug 2018

After the major rewrite of react-navigation, integrating with Redux is discouraged. There are, however, new tools to allow you to control navigation from anywhere. See, for example, how to create a NavigationService that provides this functionality

No Longer Valid - Don't do this

You haven't specified whether your navigation state is held in Redux, so if not, you should definitely integrate react-navigation and Redux.

The react-navigation documentation on how to do it is pretty clear.

After that, navigating becomes just another dispatch, only the actions come from react-navigation:

dispatch(NavigationActions.navigate({
  routeName: 'dashboard',
}));
2
votes
import firebase from 'firebase';
import { NavigationActions } from 'react-navigation';


// Login actions
export const LoginAction = (payload) => {
  return (dispatch) => {
    firebase.auth().signInWithEmailAndPassword(payload.email, payload.password)
      .then(user => loginSuccess(dispatch, user))
      .catch((error) => {
    loginFail(dispatch, error);
    });
  };
};

// Login success function

logoutSuccess = (dispatch, user) => {
  // dispatch an action
  dispatch({
    type: 'LOGOUT_SUCCESS',
    payload: user
  });  
  const navigateAction = NavigationActions.navigate({
      routeName: "dashboard",
      params: {},
      action: NavigationActions.navigate({ routeName: "dashboard" })
    });
  this.props.navigation.dispatch(navigateAction); // this is where you use navigation prop
};

// add this nav reducer to your root reducer

import Root from 'your navigation root path';

const navReducer = (state, action) => {
  const newState = Root.router.getStateForAction(action, state)
  return newState || state
}

export default navReducer;
2
votes

Solution for v2 in 2018:

1) Define a NavigationService.js file

import { NavigationActions } from 'react-navigation';

let navigator;

 function setTopLevelNavigator(navigatorRef) {
  navigator = navigatorRef;
}

function navigate(routeName, params) {
  navigator.dispatch(
    NavigationActions.navigate({
       routeName,
       params,
     }),
  );
}


 export default {
  navigate,
  setTopLevelNavigator,
 };

2) Inside App.js you should have something like this: (redux provider, the store and the top level navigator set up before)

import AppNavigation from 'route-to-NavigationService';

<Provider store={store}>
    <TopLevelNavigator
      ref={(navigatorRef) => {
      NavigationService.setTopLevelNavigator(navigatorRef);
      }}
    />
</Provider>

3) Inside from your action: import NavigationService from 'route-to-NavigationService';

function logIn(){
     //async code
     ...
     dispatch(() => {
          NavigationService.navigate('RouteNameToNav'),
     });
}

Extra: If you want to reset your navigation stack (i.e Login should reset navigation stack), then inside your dispatch, your should do:

  dispatch(() => {
      StackActions.reset({
        index: 0,
        actions: [NavigationService.navigate('App')],
      });
    });
2
votes

I have a quik and easy solution:

In the case where you have to navigate to specific screen, you can pass the navigation as a param to your redux action creator function

Example of lougout action:

Redux action:

export function handleLogout(navigation) {
    return (dispatch) => {
        dispatch(handleLoggingOut(true));
        setTimeout(() => {
            STORAGE.removeData('@FunnyLive:token').then(() => {
                dispatch(handleLoggingOut(false));
                navigation.navigate({routeName: 'Auth'});
            })
        }, 2000);
    }
}

mapDispatchToProps:

const mapDispatchToProps = (dispatch) => ({
  logout: (navigation) => dispatch(handleLogout(navigation))
})

Finally you have to call the action like this:

() => this.props.logout(this.props.navigation);
0
votes

one hacky way that worked for me is that you can send navigation prop to your action

<Button rounded block onPress={() => this.props.nav(this.props.navigation)}>
    <Text>
    nav
     </Text>
</Button>

then in your action file:

export function navAction(navigation) {
    return (dispatch) => {
        navigation.navigate('yourRoute')
    }
}
0
votes

In navigation 5 and Redux

for examle I have component Login

const LoginScreen = (props) => {
        return (
                <View style={styles.continer}>
                    <Login navigation={props.navigation}/>
                </View>
        )
}

in component Login i have function login

import { connect} from 'react-redux';
import {userLogin} from './action/userAction';

const login =()=>{
    const {username,password, navigation } = props;
    userLogin(username,password, navigation )
}


const mapStateToProps = (state) => {
   return{
       username:state.auth.username,
       password:state.auth.password,
   }
  };



export default connect(mapStateToProps,{userLogin})(Login) ;

and in userAction file

export const userLogin = (username, password, navigation) => {
    return dispatch => {
        // .... any code
        loginSucess(dispatch, navigation)
    }
}

const loginSucess = (dispatch, navigation) => {
    dispatch({
        type: USER_LOGIN_SUCSESS,
    })
    navigation.replace("Home")
}