React Native tips and tricks I've learned over the past year
May 10, 2018
It’s been over a year since I embarked on the React Native journey, and since then I’ve built a few different types and sizes of apps for iOS and Android. React Native has evolved quickly over the last 3 or 4 years, introducing new features and updates to run alongside it’s big brother, React.
It’s been an exciting project to work with because of how powerful it can make you feel, especially when you have clients/project managers trying to keep costs down, but this power comes with a price - the vast wilderness of tecnhiques, best practices and “gotchas” one must learn when dealing with such an emerging technology.
This post is a collection of tips I’ve discovered whilst hacking away at my keyboard hours on end, most of which I wish I’d come across sooner as I perhaps went “the long way round” to reach them. Nevertheless, distilling these into a simple post will hopefully help a few devs out there.
Use a file structure which makes sense to you
When I first started learning React Native, I was obsessed with nailing down the perfect file structure. I scoured the web for peoples opinions, suggestions, repos etc. but most just didn’t feel right. After developing with React Native for a while, I believe the file structure truly depends on the type of app, size of app, and what makes you feel comfortable, although that statement almost feels like a cliché at this point.
I often use a similar project structure between the larger apps I’ve developed because it means modules/components can be re-used from app to app. Re-using code between front end components works especially well when the back end is simialr also!
Here are some quick tips about structuring which I usually follow:
- Compartmentalise related functionality into directories.
- Have a directory for components and a directory for screens. Screens can be connected to the global store and pass data/functions down to components, for example.
- Have a directory to handle data processing functions in to services or modules, for example an “auth” module, and “todo” module in a Todo type app.
- Keep style files related to screens/components using the same name - for example
screens/home.js
andscreens/home.style.js
.
This file structure is not for everyone as, like I mentioned, people feel comfortable with certain structures. I’m an advocate of keeping a folder structure as clean and intuitive as possible for use by other developers.
For a more in-depth look at some file structure’s I’ve used in apps before, take a look at a some of my Github repos like jwt-react-native-boilerplate and preact-jwt-apollo-boilerplate.
Styling can get out of hand - here are some tips
Styling is not the easiest to use proficiently (or at least feel like you’re styling proficiently) because a React Native app is not CSS rendered. As you may already know, CSS-like Javascript objects are used instead and are rendered natively on either iOS or Android platform.
If coming from a web background, it can be strange to get used to a lack of the cascade and different styling properties in React Native, so here are a few tips I have accumulated.
Global styling
Global styles can be very useful is used right. This can be done in a few ways, but generally I like to do something like:
// App.style.js
import { StyleSheet } from "react-native"
export const globalStyle = StyleSheet.create({
primaryColor: "#428c55",
secondaryColor: "#58bfc6",
})
Then in your screen or component file:
// screens/Home/Home.js
import { globalStyle } from "../../App.style"
This will enable you to effectively leverege some sort of theming to easily change colours and general, high level styling. In addition to this, you can also make separate files for related parts of styling - for example, button styling with a Button.style.js
file.
Although global styling often means using utility classes (for example giving elements a class of “bold-text” to give that element a
font-weight: bold;
type property), I find it’s best not to use too many of them. React Native promotes an intuitive component-level styling much like CSS modules, so be careful when using global styles.
Assigning multiple styles to a component
It can often be useful to apply multiple styles to an element, for example if you’re assigning a global style and a style generated for a component specifically. This can be done quite easily using an array:
<Text style={[styles.overviewText, styles.whiteText]}>
Finn the cat.
</Text>
const styles = StyleSheet.create({
overviewText: {
...
},
whiteText: {
color: '#ffffff'
}
});
Stylesheet preset properties
I didn’t find out about this one for a long time and it’s so useful! There are 3 built-in pre-set styles shipped with React Native to help with common tasks:
- hairlineWidth will get a line (such as a border) to be the smallest possible width based on the device. As devices have different resolutions and screen sizes, having a
borderWidth: 1
is not alwayd the best option:
...
separator: {
borderWidth: StyleSheet.hairlineWidth,
borderTopColor: 'rgba(255,255,255, 0.6)',
marginHorizontal: 5
},
...
- absoluteFill and absoluteFillObject can be used to position an element at the back of your screen and full screen size. This is really helpful for when styling full screen maps and other components:
const styles = StyleSheet.create({
map: {
...StyleSheet.absoluteFillObject,
backgroundColor: "transparent",
},
})
Keep styles external
When developing smaller apps I find it easy to include your style definitions in the same file as it’s corresponding component, but as apps get larger and more complex with more components, I find it makes sense to import styles from another file:
I think this is how Angular CLI handles components and styling I think. It’s been a while since I’ve used Angular but it certainly was like this in Angular V2.
Keeping styles outside of component files is great for keeping your component files clean, and also promotes importing styles between files if that’s your jam.
Your navigation library is a crucial choice
Many libraries have been and gone when it comes to navigation in React Native. Back in the day there was the Navigator iOS solution and a corresponding Android only solution, but these have lost a lot of traction I feel.
In my opinion there are a small number of real contenders in the last 12 months or so:
- react-native-navigation is by far my favourite solution by the folks over at Wix. This is a purely native solution and enables you to use the native UI libraries from each platform. The benefit here is that it’s super performant as it’s truly native.
- native-navigation is another fully native solution by Airbnb. While this solution looks decent, I’ve not used it in enough detail to comment on it’s inner workings and benefits. I have noticed though that the following isn’t as strong, and their documentation is not as good as the Wix solution.
- react-navigation is a developed by the React community, and is promoted for use in the official React Native docs as it’s so easy to get up and running. This is a purely Javascript solution which makes it easy to install and use, as well as beign extremely customisable and flexible. I’ve used this solution a few times a while back and I noticed there was a small lack of performance, but I’ve heard recently (on a React Native podcast - more info to follow in this post) this has changed and is much better now to the point where you probably won’t be able to tell if it’s native or not.
Forms can be implemented with libraries to make like easier
It’s likely you’ll need to add a form to your app at some point to receive user input. This can be done without the use of libraries, but it can often mean repeating a lot of code and can be labourious if there are many fields. As with normal React JS, this means managing form inputs with component state and it can get “icky”.
There are libraries to help with these issues, as well as provide functionality which can be pretty outstanding. One such library is Formik, which abstracts almost all form interactions you can imagine into self-contained Higher-order Components or Render Props. Formik helps with every-day form features like validation, error handling, loading statuses, change/blur/submit events etc. and makes it easy for the developer.
I’d used Redux Form previously, but have been using Apollo more lately so Formik is a great package for being independant of dependencies also. According to the Formik Github page (and Dan Abramov by extension), Formik is also more performant and suitable for React form management!
Separation of concerns has never been so important
It’s been drilled in to software developers since day one - Separation of Concerns (SoC)! Keeping related parts of an application segmented from each other to promote easy testing, re-usability, and easy-to-understand code.
With React and React Native SoC is not promoted in and formal structure as React’s JSX/Class based architecture leads you to write functionality and logic in the same place as your JSX and visual code. A lot of tutorials show adding promise callbacks within the UI component, which I dont like because it clutters up the code, and mixes distinctly different concerns together.
Separating your code into related chunks can be done many ways in React Native, but I tend to do this differently depending on my setup. For me With React Native specifically, chosing a global state management solution is a huge decision, and different tools/techniques allow you to separate your concerns nicely. My 2 favourite React Native global state tools are Redux and Apollo.
As a general rule for both Redux and Apollo, I keep all functions directly related to the UI inside the React class. This includes things like the normal lifecycle methods, Flat List functions, event handler functions like clicks etc. Aside from these though, there are the functions which process data, often asynchronously. I like to keep these out of my React class as much as possible:
Redux separation of concerns
With Redux, most of these data processing functions, such as calls to the server, can use Redux Thunk to dispatch to the reducer upon a server response. This means all these functions can be called from another file.
...
import { login, checkAuthTest } from '../modules/auth/auth.service';
class Login extends Component {
constructor(props) {
super(props);
}
render() {
const { handleSubmit } = this.props;
const submitForm = e => {
this.props.login(e.email, e.password);
};
return (
<View>
// form fields
<Button
onPress={handleSubmit(submitForm)}
title={'Log in'}
/>
<Button
onPress={this.props.checkAuthTest}
title={'Check restricted access'}
/>
</View>
);
}
}
...
function mapDispatchToProps(dispatch) {
return {
login: (email, password) => {
dispatch(login(email, password));
},
checkAuthTest: () => {
dispatch(checkAuthTest());
}
};
}
let LoginConnect = connect(mapStateToProps, mapDispatchToProps)(Login);
For this Redux snippet, I’ve imported functions from a service file which handles as many functions I can reasonably include where it makes sense. These functions from the service file are imported, and dispatched using Redux’s connect()
.
Apollo Client separation of concerns
Apollo (React Apollo specifically) handles data in a more imperative way by having data passed down to it, again using a higher order component similar to the connect()
from Redux:
...
import { saveTokens } from '../../modules/auth/auth.service';
class Login extends Component {
constructor(props) {
super(props);
}
render() {
return (
<View>
// form fields
<Button
onPress={this.props.loginUser}
title={'Log in'}
/>
<Button
onPress={this.props.checkAuthTest}
title={'Check restricted access'}
/>
</View>
);
}
}
const loginMutation = gql`
mutation($email: String!, $password: String!) {
loginUser(email: $email, password: $password) {
authToken
refreshToken
}
}
`;
const gqlWrapper = graphql(loginMutation, {
props: ({ mutate, ownProps }) => ({
loginUser: (userDetails, setSubmitting) => {
mutate({
variables: { ...userDetails },
update: (proxy, { data: { loginUser } }) => {
let { authToken, refreshToken } = loginUser;
saveTokens(authToken, refreshToken)
}
})
}
})
});
export default gqlWrapper(Login);
With Apollo, all data processing information can be kept outside of the React class inside the Apollo graphql()
function. Again, functions can then be imported from an external service file to keep general functionality out of the component file all together, similar to the Redux solution.
Apollo Client has had a recent update to version 2 a month or so back. This makes a move to use Query and Mutation components directly in the
render()
method, which I don’t feel is separates concerns quite enough for larger apps. Just something to be aware of.
Use the right tools for the job
As with any part of software development, there’s an abundance of tools developed by the people, for the people. React Native is no different, and the tools and helper packages are so extensive I’ll list only the most helpful ones I use here:
- React Native Elements is a great set of pre-built small components for use across both Android and iOS. This is useful because it’s so simple to implement, and there are all sorts of components from buttons and icons, all the way up to search bars and sliding panels.
- React Devtools is covered in the official React docs, and is a good way to browse your component tree as you would using your browser when developong a web application. You’re able to see the props and state of a component, as well as the basic styling.
- React Native maps, along with all the best libraries, in my opinion, is a fully native implementation between Android and iOS. This map project is excellent and very flexible, taking creating and customising maps to the next level with it’s declarative nature.
Updating React Native is a pain
If you have a long-standing app, you may want to upgrade your React Native version in order to take advantage of new features. Although this may seem easy (and it can be easy if you’ve not ejected your app), it can be difficult if you’ve installed native modules which are linked/bridged to native code. This includes packages like Google Maps, React Native Navigation etc.
I found this out the hard way because I tried to upgrade React Native from V 0.47 to 0.55 and everything came crashign down. Luckily I’d committed my recent changes to Git and was able to roll back. In the end, I found this useful link to assist in a solid upgrade plan which involves unlinking native packages, upgrading and re-linking.
Don’t get put off by how complex some example apps are
It’s common to browse other repo’s on Github or where ever when looking for ways to implement new techniques or patterns, and this can often be daunting as it’s viewing code which is not familiar. As React Native specifically has a few different ways to approach a problem, try not to get disheartened by other peoples work. It takes time to build yourself a solid understanding of how to architecture a large application, and it doesn’t happen overnight.
Learning React Native can help level up your Javascript
This is true for dealing with React as well as React Native - using these frameworks may introduce you to concepts and paradigms like Functional Programming, for example. If you’re a seasoned JS developer you may already know concepts like currying, higher order functions etc., but some of these concepts are hard to avoid if you’re using React/React Native an advanced level.
As well as Functional Programming concepts (which you can read more about in a previous post), React Native also makes use of the most modren features of the Javascript language it’s self with the use of Array and Object Destructuring, Javascript Classes, and many more.
One of the important parts about learning new Javascript principles mentioned here is to make sure you put the time and effort in to learning why you’re using these new features, not just using them for the sake of it, and also understanding the alternatives.
Listen to podcasts!!
I’m a huge fan of podcasts for software development, and React Native is no different! The folks over at Devchat.tv have a great podcast called React Native Training which discusses all things about the RN platform, with the occasional special guest. It’s a great way to keep up to date with the platform, and pass the time on a boring commute to work.
Until next time
Thanks for reading - if you have any suggestions/tips for devloping with React Native, drop me an email or Tweet and let me know!
Senior Engineer at Haven