Get started with Redux in React Native
genkio opened this issue · 0 comments
genkio commented
Explained using a expo powered Facebook login implementation.
Install dependencies
$ yarn add redux react-redux redux-thunk
# react-redux to bind react and redux
# redux-thunk to handle async action creators
Create store
// ./store/index.js
import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducers from '../reducers';
const store = createStore(
reducers,
{}, // default state
compose(
applyMiddleware(thunk)
)
);
export default store;
Create reducers
// ./reducers/index.js
import { combineReducers } from 'redux';
import auth from './auth_reducer';
export default combineReducers({
auth
});
// ./reducers/auth_reducer.js
import {
FACEBOOK_LOGIN_SUCCESS,
FACEBOOK_LOGIN_FAIL
} from '../actions/types';
export default function(state = {}, action) {
switch (action.type) {
case FACEBOOK_LOGIN_SUCCESS:
return { token: action.payload };
case FACEBOOK_LOGIN_FAIL:
return { token: null };
default:
return state;
}
}
Create actions
// ./actions/types.js
export const FACEBOOK_LOGIN_SUCCESS = 'facebook_login_success';
export const FACEBOOK_LOGIN_FAIL = 'facebook_login_fail'
// ./actions/auth_actions.js
import { AsyncStorage } from 'react-native';
import { Facebook } from 'expo';
import {
FACEBOOK_LOGIN_SUCCESS,
FACEBOOK_LOGIN_FAIL
} from './types';
// action creator
// thunk requires return function(dispatch) { ... }
// refactor with ES6's syntax sugars (automatically return)
export const facebookLogin = () => async dispatch => {
let token = await AsyncStorage.getItem('fb_token');
if (token) {
// Dispatch an action saying FB login is done
dispatch({ type: FACEBOOK_LOGIN_SUCCESS, payload: token });
} else {
// Start up FB Login process
doFacebookLogin(dispatch);
}
};
const doFacebookLogin = async dispatch => {
let { type, token } = await Facebook.logInWithReadPermissionsAsync(`${fb_app_id}`, {
permissions: ['public_profile']
});
if (type === 'cancel') {
return dispatch({ type: FACEBOOK_LOGIN_FAIL });
}
await AsyncStorage.setItem('fb_token', token);
dispatch({ type: FACEBOOK_LOGIN_SUCCESS, payload: token });
};
// ./actions/index.js
export * from './auth_actions';
main.js setup
// main.js
import { Provider } from 'react-redux';
import store from './store';
// the Provider component accepts a store as prop, it makes the store available to all its children component
class App extends React.Component {
render() {
return (
<Provider store={store}>
<MainNavigator />
</Provider>
);
}
}
Children component to consume
// AuthScreen.js
import React, { Component } from 'react';
import { View, Text, AsyncStorage } from 'react-native';
import { connect } from 'react-redux';
import * as actions from '../actions';
// or import only the facebookLogin action creator
// import { facebookLogin } from '../actions';
class AuthScreen extends Component {
componentDidMount() {
this.props.facebookLogin();
this.onAuthComplete(this.props);
}
componentWillReceiveProps(nextProps) {
this.onAuthComplete(nextProps);
}
onAuthComplete(props) {
if (props.token) {
this.props.navigation.navigate('map');
}
}
render() {
return (
<View />
);
}
}
function mapStateToProps({ auth }) {
return { token: auth.token };
}
export default connect(mapStateToProps, actions)(AuthScreen);
Lastly, let's hit it home with a diagram I found on the internet, which illustrates the Redux flow.