Redux and React Native, simple login example flow
In React applications, information is stored in the local state (this.state = {}) and passed down from parents components to children components through props (props stands for properties). This architecture is fine for small applications but quickly becomes problematic for larger applications.
An easy example is the state of your user information. Upon loggin in, information about the user needs to be shared across various components which may not have any direct relationships. Keeping up your user information accross 30 components will get convoluted.
Redux introduces the concept of a global store which lives at the highest level of you application and can pass down data to all components, this is a great solution to share state across your application.
Redux comes with trade-off, so this is up to you to decide when you need it. 😅
Let’s go over some core principles ( which are the constraints you need to follow to implement Redux in your application).
- The state of your whole application is an object tree within a single store, this is the single source of truth.
- The state is read-only, the only way to change it is to emit an action. An action is a plain object (we will come back to this)
- Changes to the state are made through pure functions called reducers. Reducers take the previous state and an action, and return the next state.
Let’s now go over a simple login and user information example.
The structure of your files should follow
actionsCreators
reducers
containers
components
App.js
1. Actions Creators and Actions
Actions are payloads (vs protocol overhead) of information that send data from your application to your store. They are the only source of information for the store.
Action creators are functions that create actions, they simply return an action.
In the following snippet, I am defining an action creator that “gets a user information” and puts it in the payload key of the action plain object.
getUser is the action creatorexport const getUser = user => ({
type: 'GET_USER',
payload: user,
});the action is
{
type: 'GET_USER',
payload: user,
}
Think about it this way, upon logging in successfully, I will get a user object back from my back-end, let’s say in the following form
const user = {firstName, LastName}
and I want to store this user in my global store for easy access. This information will be passed to my payload key of my action.
2. Reducers
Reducers specify how the application’s state changes in response to actions sent to the store. Actions only describe the fact that something happened, but don’t describe how the application’s state changes.
The reducer is a pure function that takes the previous state and an action, and returns the next state. Note : It’s called a reducer because it’s the type of function you would pass to Array.prototype.reduce().
Two important points about reducers:
-It does not mutate the state ❕
-The previous state is returned in the default case
In the following, our reducer is taking the previous state user : {} and as empty object and returning action.payload (my user information defined above), the default case is return the previous state, my empty object.
const user = (state = {}, action) => {
switch (action.type) {
case 'GET_USER':
return action.payload;
default:
return state;
}
};export default user;
Considering this is very likely you will have a list of reducers, Redux has a CombineReducers
object, where you can pile all your reducers like in the following:
import { combineReducers } from 'redux';
import user from './user';
import session from './session';
import homeworks from './homeworks';
import badges from './badges';const rootReducer = combineReducers({
user,
session,
homeworks,
badges,
});export default rootReducer;
3. The Store
So far, we have defined
— the actions that represent the facts about “what happened”
— the reducers that update the state according to those actions
The store has the following roles:
-Holds application state;
-Allows state to be updated via dispatch(action);
-Registers listeners via subscribe(listener);
We create it from the rootReducer defined above like so const store = createStore(rootReducer)
and you make it available to your entire application through
<Provider store={store}>
<App /> // Root component of your application
</Provider>
4. The containers
We have ‘presentational’ components (components that describe the look of your data and render the information given to them) and ‘container’ components (they connect our ‘presentational’ components to Redux to get access to the store).
Containers components can
>read the global state and supply it to presentational component as props
>dispatch action and supply it to presentational component as props
- Read the state
Container component are React component that usesstore.subscribe()
or reallyconnect()
fromreact-redux
to read a part of the Redux state tree and supply props to a presentational component it renders.
To supply props, you need to define a special function called ‘mapStateToProps ’that tells how to transform the current Redux store state into the props you want to pass to a presentational component you are wrapping.
2. Dispatch actions
We define a function called ‘mapDispatchToProps’ that receives the dispatch() method and returns callback props that you want to inject into the presentational component.
import { connect } from 'react-redux';
import { getUser } from '../actions/actions';
import LoginComponent from '../components/LoginComponent';//Disclaimer: The mapStateToProps is only for the sake of the example, this is a LoginComponent that will feed the state with the user object, and thus does not need the state passed down to its props
mapStateToProps = state => ({
user: state.user,
});const mapDispatchToProps = dispatch => ({
onLogin: (user) => {
dispatch(getUser(user));
},
});const LoginContainer = connect(
mapStateToProps,
mapDispatchToProps)
(LoginComponent);export default LoginContainer;
5. The presentational component
<View style={styles.container}>
<Text h4>Student Login</Text>
<FormLabel>Enter your Email</FormLabel>
<FormInput
onChangeText={text => this.setState({ userName: text })}
/>
<FormLabel>Password</FormLabel>
<FormInput
onChangeText={text => this.setState({ password: text })}
/>
<Button
onPress={this.handleSubmit}
buttonStyle={[{ marginBottom: 5, marginTop: 5 }]}
title="Login"
/>
</View>handleSubmit() {
const { user } = this.state;
this.props.onLogin({ user });
The presentational component get the onLogin passed down through its props, and we access it with ‘this.props.onLogin’. We give it the user object, that will be dispatched to our golobal store!