Design Practices for React Native App

Design Practices for React Native App
Best Design Practices for React Native Application

After spending nearly 6 years building applications with React-Native, I learned a lot of lessons hard way. This blog will help you to not commit the mistakes that I did and your codebase will be in good shape any time you look at it. This blog is for the people who have some knowledge of react-native and they are planning to start with a big project.

Let’s jump directly onto the steps you need to follow.

  1. IDE or Editor: Choosing you’re editor is one of the first steps to start with your codebase. I prefer Webstorm for developing the code base for react-native applications because it by default sets up all the configuration for the project. But, Webstorm costs you around $12.9 per month if you are familiar with Android Studio this will be a good choice. Whereas, Visual Studio Code is free of cost and provides you an option to install thousands of plugins in your IDE. But for setting up eslint as per your requirement will take you a bit of extra effort.
  2. Code Linter: If you are going forward with Webstorm there is no need to install code linter, it sets up a linter for you by default. For VS code you need to go through a small setup process. After setting up eslint in VS code checkout their advanced guide for more configuration as per your react-native project. You can use Airbnb pre-configured eslint package if you are facing any setup issues.
  3. Global Constants: Your project code base will become huge before you even know it, don’t make the mistake of putting your global constants in every file you use them. Instead, create a global file for your constants like app colorsfont sizes, and other fonts related properties, app URLs, base fetch or axios API calls, etc.
  • Styling properties like width, height, etc. can be written in the below form. The best way is to use density-independent pixel(dp) while writing the styling of your app.
const widthPercentageToDP = widthPercent => {
    const screenWidth = Dimensions.get('window').width;
    const elemWidth = parseFloat(widthPercent);
    return PixelRatio.roundToNearestPixel(screenWidth * elemWidth / 100);
};
const heightPercentageToDP = heightPercent => {
    const screenHeight = Dimensions.get('window').height;
    const elemHeight = parseFloat(heightPercent);
    return PixelRatio.roundToNearestPixel(screenHeight * elemHeight / 100);
};
const proportionedPixel = designPixels => {
    const screenProportion = Dimensions.get('window').width / 180;
    return PixelRatio.roundToNearestPixel(designPixels * screenProportion);
};

StyleProperties.js

  • Font related properties like size can be used in the below format and similarly it can be done for font-family, font-weight, etc.
const fontSize = {
    mini: proportionedPixel(4.5),
    small: proportionedPixel(5),
    medium: proportionedPixel(6),
    large: proportionedPixel(7),
    xLarge: proportionedPixel(8),
    xMidLarge: proportionedPixel(9),
    xxLarge: proportionedPixel(20)
};
  • There are going to be a lot of API calls in your application. Do not fill up your actions with API calls and their response failure or success. Instead, keep a base axios and use it to import to all the application actions.
import axios from 'axios';
import { USER_NAME, PASSWORD, REST_END_POINT } from './ApiConstants';

function baseAxios(options) {
    const defaultHeaders = {
        'Content-Type': 'application/json',
        'Accept-Language': options.language ? options.language : 'en',
        'lang': options.lang ? options.lang : 'en',
        username: USER_NAME,
        password: PASSWORD,
        ...options.extraHeaders
    };

    const baseUrl = REST_END_POINT;
    return axios.create({
        baseURL: baseUrl,
        timeout: options.timeout || 30000,
        headers: defaultHeaders,
    })
}

function executeRequest(method, pathname, data, options = {}) {
    const body = method === 'get' || !data ? {} : {
        data
    };
    const reqObj = {
        method,
        url: pathname,
        params: options.query,
        ...body
    };
    const baseAxiosRequest = baseAxios(options)
    return new Promise((resolve, reject) => {
        return baseAxiosRequest
            .request(reqObj)
            .then(res => {
                console.log('Axios', res);
                resolve(res);
            })
            .catch(error => {
                console.log('Axios', error.response);
                reject(error);
            });
    })
}

export default {
    get(pathname, options) {
        console.log('BaseApi', options);
        return executeRequest('get', pathname, null, options)
    },

    post(pathname, data, options) {
        return executeRequest('post', pathname, data, options)
    },

    put(pathname, data, options) {
        return executeRequest('put', pathname, data, options)
    },

    delete(pathname, data, options) {
        return executeRequest('delete', pathname, data, options)
    },

    all(promises) {
        return axios.all(promises)
    },
}

BaseAxios.js

4. Styling Components: Styled Components has one of the most stars on Github when it comes to styling your components. You can create any of your presentational components using this library. For re-usable Custom Text and Custom Button components, you can refer to the code below.

export const CustomText = styled.Text`
   font-size: ${props => props.fontSize}px;
   color: ${props => props.fontColor};
   letter-spacing: ${props => props.letterSpacing};
   font-style: ${props => props.fontStyle};
   font-weight: ${props => props.fontWeight};
   font-family: ${props => props.fontFamily};
   line-height: ${props => props.lineHeight};
`;
CustomText.defaultProps = {
    fontSize: fontSize.medium,
    fontColor: whiteGroundColor,
    letterSpacing: '0px',
    fontStyle: 'normal',
    fontWeight: 'normal',
    fontFamily: regularFont,
    lineHeight: '18px',
};
export const CustomButton = ({
    width,
    backgroundColor,
    padding,
    borderRadius,
    clickData,
    onPress,
    textColor,
    fontSize,
    title,
    margin,
}) => (
    <ButtonWrapper
        width={width}
        backgroundColor={backgroundColor}
        padding={padding}
        borderRadius={borderRadius}
        margin={margin}
        onPress={() => onPress(clickData)}
    >
        <ButtonText textColor={textColor} fontSize={fontSize}>
            {title}
        </ButtonText>
    </ButtonWrapper>
);
const ButtonWrapper = styled.TouchableOpacity`
    width: ${props => props.width};
    background-color: ${props => props.backgroundColor};
    padding: ${props => props.padding};
    border-radius: ${props => props.borderRadius};
    border: 2px solid ${props => props.backgroundColor};
    margin: ${props => props.margin}
`;
const ButtonText = styled.Text`
    color: ${props => props.textColor};
    font-size: ${props => props.fontSize}px;
    text-align: center;
`;
ButtonWrapper.defaultProps = {
    width: '100px',
    backgroundColor: appColor,
    padding: '4px 16px',
    borderRadius: '5px',
    margin: '5px 5px'
};
ButtonText.defaultProps = {
    textColor: whiteColor,
    fontSize: fontSize.medium,
};

StyledComponent.js

5. Code Structure: Most of it comes by default with react-native project setup but a little bit of more configuration is needed to put in proper form. Make a note of using a separate .style.js file for the styles and a separate folder for presentational components, which will help you in maintaining cleaner code.

-> src
     -> actions
           -> DashboardActions.js
           -> FirebaseActions.js
     -> components
           -> Presentational
                  -> Chip.js
                  -> Chip.style.js                  
                  -> Button.js
                  -> Button.style.js
           -> Dashboard
                  -> Dashboard.js
                  -> Dashboard.style.js (all styles for dashboard)
           -> Firebase.js
     -> constants
           -> ColorConstants.js
           -> DimensionConstants.js
           -> FunctionConstants.js
     -> public
           -> images
                   -> Boy.png
     -> reducers
           -> DashboardReducer.js
           -> FirebaseReducer.js
     -> localSchema
           -> Schema.js (in used realm)

We have covered a lot of important aspects of starting with the react-native project and getting the most out of this excellent framework.

6. Conventions: These are the conventions that are preferred in the React-Native community. You can write your codebase with any type of conventions but its always good to follow the below.

actions -> DashboardActions.js # capital and plural
components file names -> Dashboard.js # capital and singular
reducers -> DashboardReducers.js # capital and plural
class names -> Dashboard # capital and plural
variables -> # camelCase
indentation -> 2 spaces
folder names for components -> dashboard # lower-case and singular
default folder names -> actions, reducer, components, android etc.

7. Mobile-app Storage: React-Native by default allows you to use AsyncStorage which is an asynchronous, unencrypted, persistent, key-value storage system for React Native. This can serve most of the purpose of your application. But if you want to use a schema-based storage system, Realm or Sqlite are good choices. Such a system will help you to sync the backend db schema with your mobile app schema resulting in simple and fast API development.

8. Notification routes: Notifications have emerged as one of the most important aspects of an application. This helps applications to engage better with their users and improve with more user retention. One way to use notification is to perform some action without opening the app itself or when the user clicks on the notification, the user goes inside the app to interact with it.

  • Actions like snoozing an alarm, sending a reply, medicine reminder, etc. can be done just from the notification. There are a few configurations that you need to do for setting it up.

9. Router: It is very important to choose the appropriate router for your application. Two libraries that are mostly used for react-native application routing are react-native-router-flux and react-native-nagivation. React-Native-Navigation has great documentation which helps in writing your application routing faster, and I recommend that over react-native-router-flux.

I will be writing in detail comparing the few libraries that I have mentioned in this blog.

In case you want to know more about other conventions and design practices to follow, comment below and I would love to answer them. HAVE FUN !!!