11 mistakes I’ve made during React Native / Redux app development

After working almost a year with React Native I decided to describe mistakes that I’ve made while being a beginner.

1. Wrong estimate

Really — estimate for the first React Native (RN) application was completely wrong! Totally!

1) You need to estimate layout for iOS and Android versions separately! Of course — there will be a lot of re-used components, but there also can be different layouts. Or even structures of application’s page for iOS and Android can differ totally.

2) When you estimate forms — you better estimate validation layout too. When you develop application in RN you need to write more code than when you develop hybrid applications using Cordova for example.

3) If you need to create an application based on a webapp which already has backend (so you need to use an already built API) — make sure to check all the endpoints that are provided by backend. Because you will need to handle logic in your app — and it should be coded properly. Understand the structure of DB, how entities are connected, etc. If you understand it — you can plan your redux store properly (will be mentioned next)

2. Trying to use already built components (buttons, footers, headers, inputs, texts) — Only my opinion.

If you check Google about already built components like buttons, footers etc. you will see, that there are a lot of libraries that you can use. It is really helpful if you have no special layout design. Just build a page using these blocks and that’s all. But if you have some special design and in this design buttons look different — you will need to set custom styles for each button. It can be a little tricky. Of course, you can wrap already built components in your components and set custom styles for them there. But I think it will be easier and much valuable for you to build your own components using View, Text, TouchableOpacity and other modules from RN. Why? You will understand how to work with RN, you will have more practice. And you will be sure that the version of components that you have built by yourself will not change. So — you are not dependent on external modules versions.

3. Not separating layout for iOS and Android

This point is useful only if you have different layouts for iOS and Android versions. If no — you can simply use Platform API provided by RN to make small checks depending on device platform.

If layouts are completely different — better separate layouts in different files.

If you name a file like index.ios.js — when assembling build for iOS RN will use this file to display iOS layout. The same story is for index.android.js.

You may ask “What about code duplication?”. Yep. You can move duplicated code into helpers. And then just reuse these helpers.

4. Wrong redux store planning.

A big mistake.

When you are planning your application you think a lot about layout. Less — about data handling.

Redux helps us store data correctly. If the redux store is planned correctly — it is a powerful tool for managing application data. If not — it can mess all things up.

When I only started building RN apps, I thought about the reducers as a data storage for each container. So if you have Sign In, Forgot Password, ToDo list pages — there should be their reducers: SignIn, Forgot, ToDoList.

After working a little with this store planning I found out that in my case it was not so easy to manage data. I had a ToDo details page. And using this store planning I’ve provided ToDoDetails reducer. And that was a HUGE mistake! Why?

When I select item from ToDo list — I need to pass data to ToDoDetails reducer. It meant using additional actions for sending data to reducer. And it was not very comfortable.

After making a little research I decided to plan the store differently. And the structure was like this:

1.Auth
2.Todos
3.Friends

Auth is used to store authorization token. That is all

And Todos and Friends reducers are used to store entities as it is easy to understand from naming. And when I go to ToDo details screen — I just search through all the ToDos by id. That is all.

For much more complicated structures I really recommend to use this planning. You will always understand what is what. And where to find this or that.

5. Wrong project structure

It is always hard to plan the project structure when you are only a beginner.

First of all — understand — is your application big? Really big? Huge? Or tiny?

How many screens do you have in your application? 20? 30? 10? 5? Hello World screen?

First structure that I have met and started implementing was like this:

Actions and reducers are separated from containers

Well, this is good if you have not a big application. For example — 10 screens maximum. If it is bigger — consider using something like this:

Actions and reducers stored with containers they are connected with

What is the difference? As you can see — first type proposes us to store actions and reducers separately from container. Second — store them together. If application is small — it is more useful to store redux modules separated from containers. If not — with containers — you will always know — which action concerns this container.

If you have common styles (like for Header, Footer, Buttons) — you can create a separate folder called “styles”, set there an index.js file and write common styles there. And then just reuse them on each page.

There are a lot of different structures. You should only find out — which of them will fit your requests better.

6. Wrong container structure. Not using smart/dumb components ideology from the beginning

When you start working with RN and init your first app, there is some code already in the index.ios.js file. If you check it you will see that styles are stored in a separate object, there are no evaluations in the render method and everything is built with modules provided by RN (View, Text).

In real life you will need to use a lot of components, not only provided by RN. You will build a lot of components to re-use them while building containers.

Consider this component:

import React, { Component } from ‘react’;
import {
   Text,
   TextInput,
   View,
   TouchableOpacity
} from ‘react-native’;
import styles from ‘./styles.ios’;
export default class SomeContainer extends Component {
   constructor(props){
       super(props);
       this.state = {
           username:null
       }
   }
   _usernameChanged(event){
       this.setState({
           username:event.nativeEvent.text
       });
    }
   _submit(){
       if(this.state.username){
           console.log(`Hello, ${this.state.username}!`);
       }
       else{
           console.log(‘Please, enter username’);
       }
    }
    render() {
        return (
            <View style={styles.container}>
                <View style={styles.avatarBlock}>
                    <Image
                        source={this.props.image} 
                        style={styles.avatar}/>
                </View>
                <View style={styles.form}>
                    <View style={styles.formItem}>
                        <Text>
                            Username
                        </Text> 
                        <TextInput
                         onChange={this._usernameChanged.bind(this)}
                         value={this.state.username} />
                    </View>
                </View>
                <TouchableOpacity onPress={this._submit.bind(this)}>
                    <View style={styles.btn}>
                        <Text style={styles.btnText}>
                            Submit
                        </Text>
                    </View> 
                </TouchableOpacity>
            </View>
        );
    }
}

How does it look?

Well, as you can see — all styles are stored in a separate module — good. There is no code duplication (for now) — good. But how often do we use only one field in form? I am not sure it’s that often. Also — button component — the one which is wrapped in TouchableOpacity — can be separated — so we could reuse it in future. And about Image. We can reuse this block in future too, so it should be moved to a separate component.

And after we did all the changes — we will get this:

import React, { Component, PropTypes } from 'react';
import {
    Text,
    TextInput,
    View,
    TouchableOpacity
} from 'react-native';
import styles from './styles.ios';

class Avatar extends Component{
    constructor(props){
        super(props);
    }
    render(){
        if(this.props.imgSrc){
            return(
                <View style={styles.avatarBlock}>
                    <Image
                        source={this.props.imgSrc}
                        style={styles.avatar}/>
                </View>
             )
         }
         return null;
    }
}
Avatar.propTypes = {
    imgSrc: PropTypes.object
}

class FormItem extends Component{
    constructor(props){
        super(props);
    }
    render(){
        let title = this.props.title;
        return( 
            <View style={styles.formItem}>
                <Text>
                    {title}
               </Text>
               <TextInput
                   onChange={this.props.onChange}
                   value={this.props.value} />
            </View>
        )
    }
}
FormItem.propTypes = {
    title: PropTypes.string,
    value: PropTypes.string,
    onChange: PropTypes.func.isRequired
}

class Button extends Component{
    constructor(props){
        super(props);
    }
    render(){
        let title = this.props.title;
        return(
            <TouchableOpacity onPress={this.props.onPress}>
                <View style={styles.btn}>
                    <Text style={styles.btnText}>
                        {title}
                    </Text>
                </View>
            </TouchableOpacity>
        )
    }
}
Button.propTypes = {
    title: PropTypes.string,
    onPress: PropTypes.func.isRequired
}
export default class SomeContainer extends Component {
    constructor(props){
        super(props);
        this.state = {
            username:null
        }
    }
    _usernameChanged(event){
        this.setState({
            username:event.nativeEvent.text 
        });
    }
    _submit(){
        if(this.state.username){
            console.log(`Hello, ${this.state.username}!`);
        }
        else{
            console.log('Please, enter username');
        }
    }
    render() {
        return (
            <View style={styles.container}>
                <Avatar imgSrc={this.props.image} />
                <View style={styles.form}>
                    <FormItem
                      title={"Username"}
                      value={this.state.username}
                      onChange={this._usernameChanged.bind(this)}/>
                </View>
                <Button
                    title={"Submit"}
                    onPress={this._submit.bind(this)}/>
            </View>
        );
    }
}

Yeah, maybe there is a little bit more code now — because we added wrappers for Avatar, FormItem and Button components, but now we can reuse all these components where we want. We can move them to separate modules and import everywhere we need them. We can add them some other props, for example — style, textStyle, onLongPress, onBlur, onFocus. And these components are fully customizable.

But be sure to not dive deep in customizing a small component so much that it will grow up to the Godzilla size. It is bad and hard to read. Really. And even if at the moment the idea of adding some new property looks like the easiest way for solving task, in future this little property may be confusing when you read the code.

And about the smart/dumb ideology. Check this out:

class Button extends Component{
    constructor(props){
        super(props);
    }
    _setTitle(){
        const { id } = this.props;
        switch(id){
            case 0:
                return 'Submit';
            case 1:
                return 'Draft';
            case 2:
                return 'Delete';
            default:
                return 'Submit';
         }
    }
    render(){
        let title = this._setTitle();
        return(
            <TouchableOpacity onPress={this.props.onPress}>
                <View style={styles.btn}>
                    <Text style={styles.btnText}>
                        {title}
                    </Text>
               </View>
           </TouchableOpacity>
        )
    }
}
Button.propTypes = {
    id: PropTypes.number,
    onPress: PropTypes.func.isRequired
}
export default class SomeContainer extends Component {
    constructor(props){
        super(props);
        this.state = {
            username:null
        }
    }
    _submit(){
        if(this.state.username){
            console.log(`Hello, ${this.state.username}!`);
        }
        else{
            console.log('Please, enter username');
        }
    }
    render() {
        return (
            <View style={styles.container}>
                <Button
                    id={0}
                    onPress={this._submit.bind(this)}/>
            </View>
        );
    }
}

As you can see — we have “upgraded” our Button component. What has been changed? Well — we replaced property “title” with a new property called “id”. And now we have some “flexibility” in our Button component. Pass 0 — it will show ‘Submit’. Pass 2 — ‘Delete’. But it is really bad.

Button was created as a Dumb component — to just display the data, that was passed to it, and to do things, that was told him on higher level. Dumb components should not know anything about things that surround them. Just simply do and show what they were told. And after our little “upgrade” we made it smart. And it is bad. Why?

What will happen if we will pass 5 as id to this component? We will need to update it so it could work with this option. And so on and so on. Dumb components should only display and do what they were told. That is all.

7. Inline styles

After working a little with layout in RN I faced a problem with writing styles inline. Like this:

render() {
    return (
        <View style={{flex:1, flexDirection:'row',        backgroundColor:'transparent'}}>
            <Button
                title={"Submit"}
                onPress={this._submit.bind(this)}/>
        </View>
    );
}

When you write like this at first you think: “OK. After checking layout in simulator — will move styles to separate module if it is OK”. And maybe that is what you really wanted to do. But.. .Unfortunately it will not happen. At least — nobody in my team did it until they were reminded.

Always write styles in separate module. It will keep you safe from inline styling.

8. Validating forms with redux

That was a mistake in my project. Maybe it will be useful in yours.

To validate form with a help of redux I needed to create action, action type, separate field in reducer. And it was really annoying.

So I decided to do it only with a help of state. No reducers, types, etc. Just pure functions on the container level. That helped me a lot — removed unnecessary functions from action file, reducer file. No manipulations with the store.

That is what fitted my project.

9. Relying on zIndex TOO MUCH

A lot of people come to RN development from web. And in web there is a CSS which has a z-index property. It helps us to display the layer we want on the level we want. In web it is really cool.

In RN there was no such feature at the beginning. But later it was added. And so I started using it. At first it was really easy. Render layer in whatever order you want and just set them zIndex property as a style. And it will work. But after testing it on Android… Now I just structure my layers in order they should display. That is the best zIndex you can do. Really.

10. Not reading code of external modules

When you want to save your time — you use external modules. Often they have documentation. You just take information from it and use.

But sometimes this module may break. Or not work as it was described. That is why you need to read the code. You will understand what is wrong. Maybe the module is bad. Or maybe you are just using it incorrectly. Plus — you will learn how to build your own module if you will read other modules code.

11. Awareness of PanResponder and Animated APIs.

RN provides us an ability to build fully native applications. And what does make application feel native? Layouts, gestures, animations.

And if Layouts are provided by default, when you use View, Text, TextInput and other RN modules, Gestures and Animations should be handled by PanResponder and Animated APIs.

If you came from web (as I did) it will be a little bit scary for you — get user gestures — when it started, when it finished, long press, short press. Also it is not clear enough — how to animate something in RN.

Here is a Button component which I built with a help of PanResponder and Animated. This button was built to capture user gestures. For example — user presses item and then drags finger to the side. With a help of Animated API was built opacity changing when button was pressed:

'use strict';
import React, { Component, PropTypes } from 'react';
import { Animated, View, PanResponder, Easing } from 'react-native';
import moment from 'moment';
export default class Button extends Component {
    constructor(props){
        super(props);
        this.state = {
            timestamp: 0
        };
        this.opacityAnimated = new Animated.Value(0);
        this.panResponder = PanResponder.create({
   onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
   onStartShouldSetResponder:() => true,
   onStartShouldSetPanResponder : () => true,
   onMoveShouldSetPanResponder:(evt, gestureState) => true,
   onPanResponderMove: (e, gesture) => {}, 
onPanResponderGrant: (evt, gestureState) => {
   /**THIS EVENT IS CALLED WHEN WE PRESS THE BUTTON**/
       this._setOpacity(1);
       this.setState({
           timestamp: moment()
       });
       this.long_press_timeout = setTimeout(() => {
            this.props.onLongPress();
       }, 1000);
   },
   onPanResponderStart: (e, gestureState) => {},
   onPanResponderEnd: (e, gestureState) => {},
   onPanResponderTerminationRequest: (evt, gestureState) => true,
   onPanResponderRelease: (e, gesture) => {
   /**THIS EVENT IS CALLED WHEN WE RELEASE THE BUTTON**/
       let diff = moment().diff(moment(this.state.timestamp));
       if(diff < 1000){
           this.props.onPress();
       }
       clearTimeout(this.long_press_timeout);
       this._setOpacity(0);
       this.props.releaseBtn(gesture);
   }
     });
    }
    _setOpacity(value){
    /**SETS OPACITY OF THE BUTTON**/
        Animated.timing(
        this.opacityAnimated,
        {
            toValue: value,
            duration: 80,
        }
        ).start();
    }
    render(){
        let longPressHandler = this.props.onLongPress,
            pressHandler = this.props.onPress,
            image = this.props.image,
            opacity = this.opacityAnimated.interpolate({
              inputRange: [0, 1],
              outputRange: [1, 0.5]
            });
        return(
            <View style={styles.btn}>
                <Animated.View
                   {...this.panResponder.panHandlers}
                   style={[styles.mainBtn, this.props.style, {opacity:opacity}]}>
                    {image}
               </Animated.View>
            </View>
        )
    }
}
Button.propTypes = {
    onLongPress: PropTypes.func,
    onPressOut: PropTypes.func,
    onPress: PropTypes.func,
    style: PropTypes.object,
    image: PropTypes.object
};
Button.defaultProps = {
    onPressOut: ()=>{ console.log('onPressOut is not defined'); },
    onLongPress: ()=>{ console.log('onLongPress is not defined'); },
    onPress: ()=>{ console.log('onPress is not defined'); },
    style: {},
    image: null
};
const styles = {
    mainBtn:{
        width:55,
        height:55,
        backgroundColor:'rgb(255,255,255)',  
    }
};

First - we init PanResponder’s object instance. There we set different handlers. What we are interested in is onPanResponderGrand (called when the user touches the button) and onPanResponderRelease(called when the user removes the finger from the screen) handlers;

Also we set up an Animated’s object instance which helps us work with animation. Set its value to zero; Then we define _setOpacity method, which changes value of this.opacityAnimated. And before render we interpolate this.opacityAnimated to normal opacity value. We use not View but Animated.View module in order to use a dynamically changed opacity value. That is all.

As you can see - it is not hard to understand, what is going on. Of course, you will need to read documentation about these APIs to make your apps perfect. But I hope this example will help you to start.

React Native is awesome. You can do mostly everything with it. If not - you can do it in Swift/Objective C or Java and then just port it to React Native.

There is a big community, a lot of solutions, components, structures and so on and so on. And when you develop - you will make mistakes, for sure. So I hope this article will help you to avoid some of them.