10 Tips for Structuring a React Native Project
When starting a new project, there are plenty of choices to be made around code style, language, folder layout, and more. Consistency is the key for creating clean, maintainable codebases. Therefore once decided, you'd usually need to stick with these choices for a while.
Time and experience will teach you what works and what doesn't. But what if you don't have time? You can always use someone else's experience.
Here are my top 10 tips for structuring a React Native project:
1. Use TypeScript
Yes, there is a bit of a learning curve if you're used to plain JavaScript.
Yes, it's worth it.
Typed JavaScript makes refactoring a whole lot easier, and when done right, gives you a lot more confidence in your code. Use the guide in the docs for setup instructions. Make sure to enable strict mode ("strict": true
in the compilerOptions
).
You can also add type checking in your CI with tsc --noEmit
, so you can be confident in your types!
2. Set up a module alias to /src
Set up a single module alias to /src
(and a separate one for /assets
if needed), so instead of:
import CustomButton from '../../../components/CustomButton';
you can do:
import CustomButton from '@src/components/CustomButton';
I always use a @
or a ~
in front of src
to highlight it's an alias.
I've seen implementations where folks set up multiple type aliases - one for @components
, one for @screens
, one for @util
etc, but I've found a single top level alias to be the clearest.
There's a handy guide for setting this up with TypeScript in the React Native docs.
3. Use Inline Styles
You have an option for using the built in inline styles, or Styled Components.
I started off with Styled Components, then switched to inline styles, because there used to be a performance implication, though that's negligible, so now it's just a preference.
4. One Style File Per Component
Each component should have their own style file with a styles.ts
extension:
FirstComponent.tsxFirstComponent.styles.tsSecondComponent.tsxSecondComponent.styles.tsx
Note, the .styles.ts
in the filename is just a convention I use to indicate that the styles belong to the component, the TypeScript compiler will treat these as regular .ts
files.
Each style file exports a single style object for the component:
// FirstComponent.styles.tsimport { StyleSheet } from 'react-native';const styles = StyleSheet.create({ container: { padding: 20, },});export default styles;
Each component only imports only its own styles:
// FirstComponent.tsximport styles from './FirstComponent.styles';...
5. Use Global Styles
Create a globalStyles.ts
file at the top level of the /src
directory, and import it to the .styles.ts
as needed.
Always use constants for:
- colours
- fonts
- font sizes
- spacing
It may seem tedious at first, but handy in the long term. And if you find you're ending up creating constant for every single space, it's something to gently bring up with the Design team, as design guides would generally not want that.
6. Flatten Style Constants
Instead of:
const globalStyles = { color: { blue: '#235789', red: '#C1292E', yellow: '#F1D302', },};
Do this:
const globalStyles = { colorBlue: '#235789', colorRed: '#C1292E', colorYellow: '#F1D302',};
It can be tempting to group these, but I've found that keeping them flat can be more handy, e.g. if you wanted to replace all instances of colorRed
in your codebase, you could do a find and replace, whereas with colors.red
it'd be harder, since the colour could have been destructured.
7. Use Numbers in Style Constants
Instead of:
const globalStyles = { fontSize: { extraSmall: 8, small: 12, medium: 16, large: 18, extraLarge: 24, },};
Do this:
const globalStyles = { fontSize8: 8, fontSize12: 12, fontSize16: 16, fontSize18: 18, fontSize24: 24,};
The first option may look nicer when writing it down, but during development, you don't tend to care about "medium" and "large", and just care about the number. And it will avoid the awkward naming when the designers inevitably add a font size 14 and you have to start calling your variables things like mediumSmall
.
8. One Component Per File
Here's the template for a new component:
import React from 'react';import { View, Text } from 'react-native';import styles from './App.styles';const App = () => { return ( <View style={styles.container}> <Text>Hello, world!</Text> </View> );};export default App;
Some things to note here:
- function components over class components: I'd always use function components and manage any state and side-effects using hooks
- I use constant functions, but both
const
andfunction
are equally good here. In factfunction
might be better in the long term - default export: I always use a default export, though there is an argument to be made that named exports are better since they'll be clearer to refactor, and I agree - that might be the next step
9. Separate Components and Screens
Here's a typical folder structure I end up with:
/assets /images image.png anotherImage.png /icons icon.svg anotherIcon.svg/src /components Component1.tsx Component1.styles.ts Component1.test.ts Component2.tsx Component2.styles.ts Component2.test.ts /screens Screen.tsx Screen.styles.ts Modal.tsx Modal.styles.ts App.tsx globalStyles.ts types.ts
I always separate components in the /components
directory and the screens and modals in the /screens
directory. When using react-navigation
, there is no structural difference between screens and modals, but I prefer to also differentiate the intent by naming the file SomethingModal.tsx
.
Another thing to note is the file names - rather than creating a folder with the file name, and naming each file index.tsx
, the filename should reflect the component name. That is mostly for convenience - in most editors, it'll get tedious to track down which file you're editing when they're all called index.tsx
I've also seen implementations where all components are imported to a single index.ts
file and exported from there. I personally am not a fan of that solution and see it as an unnecessary extra step.
10. Lint Your Code
It's worth it. Trust me!
- Use eslint and prettier - they actually come pre-installed when you initialise a new project
- Set up a pre-commit hook - I usually set up a pre-commit hook for linting and pre-push hook for tests. There's a great guide here.
- Check lint, test and TypeScript errors on CI! This is so important - the only way to ensure a consistent code style across the project lifecycle. Setting up CI is one of the first things I do when starting a new project.
Hope this helps! Got any tips of your own that I did't list here? Let me know in the comments!
Original Link: https://dev.to/kadikraman/10-tips-for-structuring-a-react-native-project-k19
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To