React Native Redux, the Right Way.

This is the React Convention™: convention over configuration step-by-step guide for the React Native and ReactJS developers.

This is the online convention and tutorial e-book for React Native Developers. Learn the React Native Redux best practises.

This e-book is 100% online (no emails required, no PDFs to downlad etc.). It contains learning materials for junior and senior front-end devs who want to master React Native Redux.

In case of any problems and feedback, feel free to contact the authors at kamil.przeorski@gmail.com or lukasz@mobilewebpro.pl

The React Convention™ Book's Content Table

Chapter #1: ReactJS

For the ReactJS content table please click here.

Chapter #2: React Native

reactjs poland community

Why to learn React Native?

Main reasons why to use: it grows like a crazy and even faster than ReactJS: The main reasony why the React Native may be a good choice for you is that it grows like a crazy:

The red line is the React Native Google Trends "interest over time". Many times when a new JS library comes to life, it doesn't get the traction and it's not worth to spend time for learning it - this graph shows a totally different story. If you are looking to be in the good place at a good time in terms of a job market then it may be it. Remember that when there is a new high growing technology which is getting traction, then the supply grows slower than a demand because it requires time to learn it. In another words, you can find a job faster because there are less programmers who knows the technology, than the companies' demand. Any other reason why you shall to learn React Native is just a cause of that good looking growth rate chart.

Now let's the focus how to learn the React Native, the right way.

Who are you? What is your background?

  • Are you a front-end developer (experienced in Angular/Ember/Backbone/jQuery etc.) who wants to start writing native mobile apps with React-Native?
  • Are you an iOS developer who heard of React-Native and want to ramp up your skills for the future demanding job market?
  • Are you an Android developer who launched many indie games but didn't get your success in the Google App Store Market and want to update his skills in order to find a better job?
  • Are you a ReactJS developer who wants to master React-Native in order to broden your sellable skills?
  • ... Or, are you simply a React-Native developer who is looking for a best possible way to structure your next mobile app?

It's good that you have found that amazing guide - you will find here everything you need to ramp up your skills... this book is going to be improved over time so please send any feedback to lukasz@mobilewebpro.pl

We will guide you on how to use the React-Native Convention™ which has almost the same project structures as the ReactJS Convention™ one. The idea is to have almost the same structure for web and mobile React projects, so you can feel better while developing in the ReactJS or React-Native technology.

Achieve faster migration from web to mobile (and vice versa)

As you can find below, the app which you are going to make in React-Native for iOS/Android platform does already exsits in ReactJS:

As you can see above on the GIF animation: based on the ReactJS tutorial, in that React Native tutorial you will build exactly the same application. ReactJS to React-Native (& vice versa) migration-first tutorial is open sourced for you and you can find it here.

The goal of the ReactJS Convention™ and the React-Native Convention™ is to provide you almost the same codebases in case, if you want to make a web application first and then migrate it to the iOS or Android platform, the quickest possible way. Here below is the copy of the ReactJS application from the above GIF animation that has been made for the iOS platform with React Native (step by step tutorial how to do it is described later):

For more reasons why to use the React Convention, check: Why you should use the React Convention™. In short explaining this part: React Convention provides you Convention over Configuration, so you team mates can be productive quicker.

If you plan to build a web and mobile version of your idea, then the React Convention is the best place to start working with.

Analyzing an optimal React Native learning path

Depending on your previous experience, the learning path will be different in order to master the React Native development.

Case #1: You are a JavaScript newbie (like an iOS or Android developer)

Let's assume that you are a JS newbie. Maybe you have an experience in C#, Swift or Android's Java. The most important advice for you is: to learn ES6 (the ES6 guide is here).

Next step is to master the ReactJS lessons (yes, probably you want to learn the React Native straight away, but please believe me that's not a good idea at this point as React Native uses exactly the same principles as ReactJS - and the best learning materials are ReactJS at this point - the best guide on how to learn the basics of ReactJS you can be found here: How to learn React and Redux basics. After you finish the React Redux's basics then you need to complete the React Convention™ guide - start from reading this: Why you should use the React Convention™.

After you have finished the tutorials linked above you can start reading the React Native Convention™'s Introduction in order to learn the React Native, the right way.

Case #2: You are a front-end developer (with experience in Angular/Ember/jQuery etc.)

In this case, you must’ve already known the ES6 part mentioned here: the ES6 guide.

You still need to learn the following:

Case #3: You are a frontend developer with experience in React Redux

In this case, get familiarized yourself with the ReactJS Convention™ in order to make your way easier with the following instructions below for the React Native Convention™.

Case #4: You are already familiar with the ReactJS Convention™, already

If so you can dive into the practical React Native part, directly.

React Native Introduction


IMPORTANT: if you have any trouble with this e-book, then please contact over the React Native Convention™ Facebook Group or send an email to lukasz@mobilewebpro.pl

You need proper development environment set to work on react-native projects. To set it correctly please follow the official instructions:

https://facebook.github.io/react-native/docs/getting-started.html

What you need for this handbook (environment: MAC, platform: iOS) is:

1) Xcode with iPhone simulator

2) React-native client

In this project the starting point is a rewritten react-redux-starter-kit project. So the react-native-redux-starter-kit is available in our repository and is our development base.

https://github.com/ReactConvention/react-native-redux-starter-kit

Your first step


After you have successfully have cloned the repo from:

https://github.com/ReactConvention/react-native-redux-starter-kit

Then you need to run that starter kit by following the default React Native guides from:

https://facebook.github.io/react-native/docs/getting-started.html

VIDEO: Running React Native Redux starter kit for the first time on OS X (Macbook) for iOS mobile development

We imagine that if you are new for the mobile development, probably you are not familiar how to use the xCode for example. This is how you can run for the first time the starter kit:

VIDEO: Running React Native Redux starter kit for the first time on Windows for Android mobile development

In progress - please join the comunity to be noticed when the video is ready: Community.

VIDEO: Running React Native Redux starter kit for the first time on Linux for Android mobile development

In progress - please join the comunity to be noticed when the video is ready: Community.

While running that react native starter kit you can find a home scene and a counter scene which looks as the following gif animation:

starter counter
NOTE: this animation comes from recording the app in the iOS simulator

This is a simple application with a counter, and throughout this e-book you will implement other views and components which will help you understand the whole codebase.

To explain the whole code structure, we will start with developing a new Dashboard view with some basic features. Then we will implement a registration and login which will ensure that you know how to do it just on your own.

First step is to clone the starter kit and follow the initial instructions:

https://github.com/ReactConvention/react-native-redux-starter-kit

After you have cloned the repo, then we can start our react-native fun.

General client codebase structure


We will focus on the client-side explanation for now, so let's discuss the "src" directory that has following structure:

── src                      # Application source code
   ├── main.js              # Application bootstrap and rendering
   │
   ├── components           # Reusable Presentational Components
   │
   ├── routes               # Main route definitions
   │   ├── index.js         # Bootstrap main application routes with store
   │   ├── Home             # Fractal route
   │   │   ├── index.js     # Route definitions
   │   │   ├── assets       # Assets required to render components
   │   │   └── components   # Presentational React Components
   │   │
   │   └── Counter          # Fractal route
   │       ├── index.js     # Counter route definition
   │       ├── container    # Connect components to actions and store
   │       └── modules      # Collections of reducers/constants/actions
   │
   ├── scenes               # Components that dictate major app structure
   │   ├── router.js        # Scenes wrapper
   │   └── scenes.js        # Scenes definitions
   │
   ├── static               # Static assets
   │
   └── store                # Redux-specific pieces
       ├── createStore.js   # Create and instrument redux store
       └── reducers.js      # Reducer registry and injection

If you were working on any FLUX or redux projects, then the project structure shall be quite familiar to you. You will learn all the things located in that project step-by-step (bottom-up approach).

Don't worry if you don't get the structure so far, you will ramp up with knowledge about it during following the instructions. We will also explain each step so you will learn by doing.

Let's start with implementing a new scene called "DASHBOARD".


reactjs poland community


New dashboard scene - create dashboard route


Copy the Counter route directory and rename it as Dashboard (location is src/routes/*):

animated dashboard


Next step is to rewrite counter files to dashboard files:

a) rename variables with name "counter" to "dashboard"

b) We will rename action called COUNTER_INCREMENT to DASHBOARD_INCREMENT (this will be a number of visits to dashboard during one sessions without refreshing the browser)

Generally all code below are simply copies of Counter's router files renamed to Dashboard's.

Dashboard reducer: code1

Dashboard container: code2

Dashboard index: code3

Create dashboard component


Again we can copy the directory from src/components/Counter and name it Dashboard. Then rename all the variables and values related to counter's route as below: code4

Connect dashboard to App


To connect our new created dashboard view to the App we need insert dashboard route into src/routes/index.js, as presented below: code5

Project creates scenes automaticly base on the routes index.js file, so u don't need to worry about the scenes creator. (You can see and change this mechanism here: src/scenes/scenes.js)

Next step is to create a link to Dashboard View in Home View, as presented below: code6

After you refresh the App it should work like this:

animated dashboard
NOTE: this animation comes from recording the app in the iOS simulator

As you can see, there are two different routes with different reducers, so counter and dashboard keeps their values separately, but the mechanism is exaclty the same.

All new changes u can find in this commit: https://github.com/ReactConvention/react-native-redux-starter-kit/commit/55af911b5c7e6ea63e9e89aeb05d32fa6cbb8c34

Change dashboard functionality


What we want in dashboard view is a counter of user visits in dashboard view. To do it we need to increment the dashboard visit's counter each time the dashboard view is shown, so when component is mount to the view.

First we need to change dashboard reducer for new functionality, as presented below: code7

Next, we need changes in dashboard container, as presented below: code8

We have introduced statefull DashboardContainer - we need it to do this that way, cause we are using componentDidMount for invoking the function called this.props.visitsIncrement().

Finally, we need changes in dashboard component, as presented below: code9

IMPORTANT: The statefull DashboardContainer component is required here because our feature (increment by one on every visit of the dashboard route) require using a componentDidMount and we assume that in the components directory we want to have only "dumb components". In the src/components we will keep only stateless components (in other words they are also called dumb components).

At this point you shall have an app which increments visit's counter on dashboard view every time u enter this view, as on the animation below:

animated dashboard
NOTE: this animation comes from recording the app in the iOS simulator

All new changes u can find in this commit: https://github.com/ReactConvention/react-native-redux-starter-kit/commit/007b2ee0b303284b6e3b2ddb01b1baee191bef88

So right now we have really simple dashboard view with new functionality. The next step is to add list to the dashboard with random items. List should have following abilities:

  • add an item to the list
  • remove an item from the list
  • edit an item from the list

Add items list to the dashboard view


First we need to create mocked objects (that will be stored in a list) inside dashboard reducer. We also need to update the state object of the dashboard reducer to store current list objects and dashboard visit's counter value. Changes are presented below: code10

Next, we need to apply those changes into dashboard container, as presented below: code11

Finally we have to create list on the dashboard component, as presented below: code12

Currently, we have a little improved dashboard with visit's counter and items list (alongside with improved dashboard's reducer):

animated dashboard
NOTE: this animation comes from recording the app in the iOS simulator

All new changes u can find in this commit: https://github.com/ReactConvention/react-native-redux-starter-kit/commit/e972d1d606312fa0b745bf55d5b81b10dd152140

Add/edit item on the dashboard list


First, we need to prepare the action and handlers in the dashboard reducer for DASHBOARD_ADD_ITEM and DASHBOARD_EDIT_ITEM, as presented below: code13

As you can find above, we have added and exported two new actions:

export const dashboardAddItem = (label) => ({
    type: DASHBOARD_ADD_ITEM,
    label
})

export const dashboardEditItem = (label, index) => ({
    type: DASHBOARD_EDIT_ITEM,
    label,
    index
})

Then, after an action is requrested from a Dashboard's component, we handle it with:

    case DASHBOARD_ADD_ITEM:
      const mockedId = Math.floor(Date.now() / 1000);
      const newItem = {
        label: action.label,
        id: mockedId
      }
      state.list.push(newItem);
      return Object.assign({}, state)

    case DASHBOARD_EDIT_ITEM:
      const newLabel = action.label;
      const index = action.index;
      state.list[index].label = newLabel;
      return Object.assign({}, state)

In both functions above, we are returining a new object with return Object.assign({}, state). Rest of the code shall be self-explaining for you.

Next, we have to inject new actions into dashboard container with proper logic, as presented below: code14

We have to define constructor with new state object and funtions declaration:

  constructor(props) {
    super(props)

    this.onChangeText = this.onChangeText.bind(this)
    this.submitAction = this.submitAction.bind(this)
    this.itemOnEdit = this.itemOnEdit.bind(this)


    this.state = {
      inputValue: '',
      editedItemIndex: null
    }
  }

The inputValue inside state keeps the current value of a text input. In the editedItemIndex inside state we keep an ID of currently edited item, if there is none in edit then we make it null.

Next we had to implement the logic of each function:

  onChangeText(newText) {
    this.setState({ inputValue: newText })
  }

  submitAction() {
    const { dashboardAddItem, dashboardEditItem } = this.props;
    const { inputValue, editedItemIndex } = this.state;

    if(editedItemIndex === null){
      dashboardAddItem(inputValue);
    } else {
      dashboardEditItem(inputValue, editedItemIndex);
    }


    this.setState({
      inputValue: '',
      editedItemIndex: null
    })
  }

  itemOnEdit(index) {
    const { list } = this.props;
    const item = list[index];
    this.setState({
      inputValue: item.label,
      editedItemIndex: index
    });
  }

I won't describe details about those functions logic. (if you think it requires more detailed description then please mail us at lukasz@mobilewebpro.pl).

Final part is the render method:

  render() {
    const { inputValue, editedItemIndex } = this.state;
    const buttonText = (editedItemIndex === null) ? 'Add item' : 'Edit item';

    return (
      <Dashboard
        {...this.props}
        onChangeText={this.onChangeText}
        submitAction={this.submitAction}
        itemOnEdit={this.itemOnEdit}
        textInput={inputValue}
        buttonText={buttonText} />
    )
  }

We are passing some callbacks to the dashboard "dumb component" - we need them to operate on the reducer actions from the component level.

Finally, we have to change dashboard component to handle all new props from parent with new functional logic, as presented below: code15

We have improved our listJSX map function to keep an clickable elements on the list, as presented below:

  const listJSX = list.map((item, i) => {
      return <TouchableOpacity key={i} onPress={()=>editLabelAction(i)}>
              <Text style={styles.buttonText}> {item.label} </Text>
             </TouchableOpacity>
  })

We have also improved the render function where we add TextInput element for typing labels and we add clickable element to submit the TextInput text, as presented below:

  return (
    <View style={styles.container}>
      <Text style={styles.text}>
        Dashboard visits: <Text style={styles.value}>{value}</Text>
      </Text>
      <TextInput
        style={styles.inputText}
        onChangeText={(text) => onChangeText(text)}
        value={textInput}
        placeholder='Type here new label' />
      <TouchableOpacity onPress={inputSubmitAction} style={styles.button}>
        <Text style={styles.buttonText}> {buttonText} </Text>
      </TouchableOpacity>
      {listJSX}
    </View>
  )

Result of above changes is presented below:

animated dashboard
NOTE: this animation comes from recording the app in the iOS simulator

All new changes u can find in this commit: https://github.com/ReactConvention/react-native-redux-starter-kit/commit/d8740011cd27cf83b465a397e4a419d406a99cd9

Login with mocked data

We will implement login functionality that works purely on front-end. First step is to prepare new session reducer which will be app-wise (it will be used all across the app). We need new directory at "src/modules" location with new session.js file inside, with the following reducer definition inside: code16

Above we have created SESSION_LOGIN_SUCCESS and SESSION_LOGIN_FAIL actions. Heart of the reducer is the loginAsync function which looks like below:

  export const loginAsync = (loginObj) => {
    return async (dispatch, getState) => {
      let loginToken = await new Promise((resolve) => {
        setTimeout(() => {
          resolve()
        }, 200)
      }).then(() => {
        console.log("loginObj", loginObj);
        if(loginObj.user === 'React' && loginObj.password === 'React') {
          return 'react' // just a mocked token
        } else {
          return 'invalid' // mocked non successful login
        }
      })

      console.log("loginToken", loginToken)

      if(loginToken !== 'invalid') {
        dispatch(loginSuccess(loginToken))
      } else {
        dispatch(loginFail(loginToken))
      }

    }
  }

The function received the loginObj which is composed of two keys 'user' and 'password'. That object will be sent to the backend server (after we will unmock now), currenly we only check if the logins are correct with:

if(loginObj.user === 'React' && loginObj.password === 'React') {

We have created also an asynchronous function (return async (dispatch, getState) => {) which awaits on the promise resolve after 200 milliseconds timeout (currently it's a mock, later it will be real POST to the server).

Last part of the above code check if the login data are correct and execute proper actions.

  if(loginToken !== 'invalid') {
      dispatch(loginSuccess(loginToken))
    } else {
      dispatch(loginFail(loginToken))
    }

The dispatch comes from the react-thunk - that means, that the loginAsync is returned immediately and waits for lazy evaluation (in our case on a respond from the server if the user has provided correct or incorrect details) and then dispatch an action depending on the server response.

We dispatch loginSuccess and loginFail actions with the loginToken value as a variable.

export const loginSuccess = (value) => ({
  type: SESSION_LOGIN_SUCCESS,
  payload: value
})

export const loginFail = (value) => ({
    type: SESSION_LOGIN_FAIL,
    payload: value
})

const initialState = {
  isNotLoggedIn: true,
  loginToken: 'none'
}

There is a flag for simplicity which handles if a user is logged in (isNotLoggedIn) and we keep in that reducer the token which shall be sent to the backend on each request while a user is logged in (the loginToken may come from the backend's JSON Web Token).

Now, we are simply importing the session reducer with import session from '../modules/session' and then we add it to the reducers object, as presented below: code17

After that we need to improve dashboardContainer.js to inject session reducer data: code18

Next we have to add new component from loginDashboard.js to dashboard.js code19 code20

Based on that all changes we have made then you shall be able to run app with login required to see the dashboard as on the animation below:

animated dashboard
NOTE: this animation comes from recording the app in the iOS simulator

All new changes u can find in this commit: https://github.com/ReactConvention/react-native-redux-starter-kit/commit/9baed90900ee15712377c28444ed9b106ca8208b

Drag and Drop list elements

In this part we would like to add new drag and drop functionality to current dashboard view list. We need to improve our dashboard by creating new dashboardDragDropList.js component file and prepare changes in dashboard.js, as presented below: code21 code22

In react-native, drag'drop events can be handled by PanResponder, and this is what we are using. PanResponder is described in official react-native documentation (https://facebook.github.io/react-native/docs/panresponder.html) so it shouldn't be a problem to understand how does it work.

Until now, we create a DashboardDragDropList component and we inject it to the Dashboard component in the place of our old list objects. Next, we have to prepare logic for list element position change event - we need to improve our dashboardReducer.js and inject changes into dashboardContainer.js. Changes are prestented below: code23 code24

In dashbord reducer we add new action DASHBOARD_CHANGE_ITEMS_ORDER to handle list elements order change with proper logic.

    case DASHBOARD_CHANGE_ITEMS_ORDER:
      let newList = [];
      action.orderArray.map((orderItem)=>{
        state.list.map((stateItem)=>{
          if(orderItem == stateItem.key) {
            newList.push(stateItem);
          }
        })
      });
      return {
        ...state,
        list: newList
      }

In DASHBOARD_CHANGE_ITEMS_ORDER action we take as an argument an array of the key's of the element on the list with the new order that will be applied. Based on this newOrderArray we change the position of the element in the list inside reducer and we apply new state to the reducer.

Additionaly we edit the DASHBOARD_EDIT_ITEM action to take key of the list element as an argument - before it was the index of the element in an array, but this is not a good solution if we mutate array positions - and because of this, we have to rename the state property in dashboardContainer from 'editedItemIndex' into 'editedItemKey'.

Finally, you shall be able to run app and see the dashboard as on the animation below:

animated dashboard
NOTE: this animation comes from recording the app in the iOS simulator

All new changes u can find in those commits:

https://github.com/ReactConvention/react-native-redux-starter-kit/commit/c75aeed451c56badd2dd3653d579f05603d61650

https://github.com/ReactConvention/react-native-redux-starter-kit/commit/b87116958d955a480e909a3afa9de05a98ae6213

A summary (front-end part)

This is react-native part of that e-book.

How do you like it? What we can improve? Please mail us with your feedback.




Search In Google For ReactJS Convention™