Monday , June 24 2019
Home / Uncategorized / How to structure your project and manage static resources in React Native

How to structure your project and manage static resources in React Native



Source: stmed.net

React and React Native are just frameworks, and do not dictate how we should structure our projects. It all depends on your personal taste and the project you are working on.

In this post, we will examine how to structure a project and how to manage local resources. This is obviously not written in stone, and you are free to apply only the pieces that suit you. I hope you learn something

For a project started with react-native init , we only get the basic structure.

C & # 39; is the ios folder for Xcode projects, the android folder for Android projects, and a index.js it's a App.js file for the React Native starting point.

ios /
Android /
index.js
App.js

As someone who has worked with native both on Windows Phone, iOS and Android, I find that structuring a project boils down to separating files gender or feature

type vs characteristic

Separating by type means that we organize files according to their type. If it's a component, there are containers and presentation files. If it's Redux, there are actions, reducers and archive files. If it is viewed, there are JavaScript, HTML and CSS files.

Group by type

redux
Actions
shop
reducers
components
container
presentation
view
javascript
html
css

In this way, we can see the type of each file, and easily execute a script to a certain type of file. This is general for all projects, but does not answer the question "what is this project about?" Is it a news application? Is it a loyalty app? Is it about monitoring nutrition?

Organizing files by type is for a machine, not for a human being. Many times we work on a function and finding files to correct in multiple directories is a hassle. It is also a problem if we plan to create a structure outside our project, because the files are distributed in many places.

Group by feature

A more reasonable solution is to organize files by feature. Files related to a function should be put together. And the test files should stay close to the source files. Take a look at this article to learn more.

A feature can be related to login, registration, integration or a user's profile. A function can contain secondary functions as long as they belong to the same stream. If we wanted to move the secondary function around, it would be easy, since all the related files are already grouped.

My typical design structure based on features is as follows:

index.js
App.js
ios /
Android /
src
screens
access
LoginScreen.js
LoginNavigator.js
onboarding
OnboardingNavigator
welcome
WelcomeScreen.js
term
TermScreen.js
notification
NotificationScreen.js
main
MainNavigator.js
news
NewsScreen.js
profile
ProfileScreen.js
search
SearchScreen.js
library
package.json
components
ImageButton.js
RoundImage.js
utils
moveToBottom.js
safeArea.js
networking
API.js
Auth.js
res
package.json
strings.js
colors.js
palette.js
fonts.js
images.js
images
logo@2x.png
logo@3x.png
button@2x.png
button@3x.png
script
images.js
clear.js

In addition to traditional files App.js is index.js and the ios1 is android folders, I put all the source files inside the src folder. Inside src I have res for resources, library for common files used through features and screens for a content screen.

As few possible dependencies

Since React Native is heavily dependent on tons of addictions, I try to be quite aware when I add more. In my project I use only react-navigation for browsing. And I'm not a fan of redux as it adds unnecessary complexity. Add an addiction only when you really need it, otherwise you're just standing aside for more problems than for value.

The thing I like about React are the components. A component is where we define vision, style and behavior. React has an online style – it's like using JavaScript to define scripts, HTML and CSS. This adapts to the functionality approach we aim for. That's why I do not use stylized components. Because styles are just JavaScript objects, we can simply share comment styles in library .

src

I really like Android, so my name is src is res to match its folder conventions.

react-native init organize babel for us. But for a typical JavaScript project, it's good to organize the files in src folder. In my electron.js IconGenerator application, I put the source files inside the src folder. This not only helps in terms of organization, but also helps to transpire the entire folder at once. Just a command and I have the files in src transpired a dist in a blink of an eye.

babel ./src --out-dir ./dist --copy-files

Screen

React is based on components. Yes. There are container and presentation components, but we can compose components to build more complex components. They usually end up showing full screen. Is called Page in Windows Phone, ViewController in iOS and Activities in Android. The React Native guide very often mentions the screen as something that covers the entire space:

Mobile apps are rarely made up of a single screen. The management of the presentation and the transition between multiple screens is usually managed by a navigator.

index.js or not?

Each screen is considered the entry point for each function. You can rename the LoginScreen.js to index.js taking advantage of the functionality of the Node module:

The modules must not be files. We can also create a Find me folder below node_modules and place a index.js file there. The same require (& # 39; find-me & # 39;) the line will use that folder index.js file

So instead of import LoginScreen from & # 39; ./ screens / LoginScreen & # 39; we can only do it import LoginScreen from & # 39; ./ screens & # 39;.

using index.js it determines the encapsulation and provides a public interface for the functionality. This is all a personal taste. Personally I prefer to explicitly name a file, hence the name LoginScreen.js.

navigator

react-navigation seems to be the most popular choice for managing navigation in a React Native app. For a feature like onboarding, there are probably many screens managed by a navigation stack, so there is no OnboardingNavigator .

You can think of Navigator as something that groups sub-screens or features. Since we group by feature, it is reasonable to place the Navigator inside the function folder. In practice it looks like this:

import {createStackNavigator} from "react-navigation"
import Welcome from & # 39; ./ Welcome & # 39;
Import term from & # 39; ./ Term & # 39;
const routeConfig = {
Welcome: {
screen: welcome
},
Term: {
screen: Term
}
}
const navigatorConfig = {
navigationOptions: {
header: null
}
}
export default OnboardingNavigator = createStackNavigator (routeConfig, navigatorConfig)

library

This is the most controversial part of structuring a project. If you do not like the name library, you can call it utility, Common, citadel , everything

This is not meant for homeless files, but is where the utilities and common components used by many features are placed. Things like atomic components, wrappers, quick fixes, networking elements and access information are used a lot, and it's difficult to move them to a specific function folder. Sometimes we just need to be practical and get the job done.

In React Native, it is often necessary to implement a button with an image background in many screens. Here is a simple one that stays inside library / components / ImageButton.js . The components the folder is for reusable components, sometimes called atomic components. According to the React naming conventions, the first letter must be capitalized.

import React by & # 39; react & # 39;
import {TouchableOpacity, View, Image, Text, StyleSheet} from & # 39; react-native & # 39;
import images from & # 39; res / images & # 39;
import colors from & # 39; res / colors & # 39;
Export the default class ImageButton extends React.Component {
render () {
return (


{} This.props.title

<Picture
source = {} images.button
style = {styles.image} />

)
}
}
const styles = StyleSheet.create ({
view: {
position: & # 39; absolute & # 39 ;,
backgroundColor: & # 39; transparent & # 39;
},
Image: {
},
tangible: {
alignItems: & # 39; center & # 39 ;,
justifyContent: & # 39; center & # 39;
},
text: {
color: colors.button,
fontSize: 18,
textAlign: & # 39; center & # 39;
}
})

And if we want to place the button at the bottom, we use a utility function to prevent duplication of the code. Here is library / utils / moveToBottom.js:

import React by & # 39; react & # 39;
import {View, StyleSheet} from & # 39; react-native & # 39;
function moveToBottom (component) {
return (

{component}

)
}
const styles = StyleSheet.create ({
container: {
flex: 1,
justifyContent: & # 39; flex-end & # 39 ;,
marginBottom: 36
}
})
export default moveToBottom

Use package.json to avoid the relative path

So somewhere in the src / screens / onboarding / term / Term.js , we can import using relative paths:

import moveToBottom from & # 39; ../../../../ library / utils / move & # 39;
Imports ImageButton from & # 39; ../../../../ library / components / ImageButton & # 39;

This is a big red flag in my eyes. It is subject to errors, as it is necessary to calculate how many .. we have to perform. And if we move the functionality, all the paths must be recalculated.

From library It is meant to be used in many places, it is good to refer to it as an absolute path. In JavaScript there are typically 1000 libraries for a single problem. A quick search on Google reveals tons of libraries to address this problem. But we do not need another dependency as it is extremely easy to solve.

The solution is to turn library in a module so node can find it Added package.json to any folder it transforms it into a node module . insert package.json inside the library folder with this simple content:

{
"name": "library",
"version": "0.0.1"
}

Now in Term.js we can easily import things from library because now it is a module:

import React by & # 39; react & # 39;
import {View, StyleSheet, Image, Text, Button} from & # 39; react-native & # 39;
import strings from & # 39; res / strings & # 39;
import the palette from & # 39; res / palette & # 39;
import images from & # 39; res / images & # 39;
Imports ImageButton from & # 39; library / components / ImageButton & # 39;
import moveToBottom from & # 39; library / utils / moveToBottom & # 39;
Export the default class Term extends React.Component {
render () {
return (

{Strings.onboarding.term.heading.toUpperCase ()}
{
moveToBottom (

)
}

)
}
}
const styles = StyleSheet.create ({
container: {
flex: 1,
alignItems: & # 39; center & # 39;
},
heading: {... palette.heading, ... {
marginTop: 72
}}
})

res

You could ask yourself what res / colors, res / strings , res / images is res / fonts are in the examples above. Well, for front-end projects, we usually have components and we model them using fonts, localized strings, colors, images and styles. JavaScript is a very dynamic language and is easy to use anywhere. We could have a lot of # 00B75D color through many files, or Fira as a font family in many Text components. This is subject to errors and difficult to refactor.

We try to encapsulate the use of resources within the res folder with safer objects. The examples below appear:

res / colors

const colors = {
title: "# 00B75D",
text: "# 0C222B",
button: "# 036675"
}
exports predefined colors

res / strings

const string =
onboarding: {
welcome: {
header: "Welcome",
text1: "What you do not know is what you have not learned",
text2: "Visit my GitHub at https://github.com/onmyway133",
button: "Log in"
},
term: {
title: "Terms and Conditions",
button: & # 39; Read & # 39;
}
}
}
export the default strings

res / fonts

const fonts = {
title: & # 39; Arial & # 39 ;,
text: "SanFrancisco",
code: & # 39; Fira & # 39;
}
export predefined characters

res / images

const images = {
button: require (& # 39; ./ images / button.png & # 39;),
logo: require (& # 39; ./ images / logo.png & # 39;),
placeholder: require (& # 39; ./ images / placeholder.png & # 39;)
}
export predefined images

Like it library , res files can be accessed from anywhere, so let's do it module . insert package.json to the res folder:

{
"name": "res",
"version": "0.0.1"
}

so that we can access resource files like normal forms:

import strings from & # 39; res / strings & # 39;
import the palette from & # 39; res / palette & # 39;
import images from & # 39; res / images & # 39;

Group colors, images, characters with the palette

The app design should be consistent. Some elements should have the same appearance and appearance so as not to confuse the user. For example, the item Text it should use a color, a font and a font size. The Image component should use the same placeholder image. In React Native, we already use the name styles with const styles = StyleSheet.create ({}) so we use the name palette.

Below is my simple palette. Defines common styles for header and Text:

res / pallet

import colors from & # 39; ./ colors & # 39;
const palette = {
heading: {
color: colors.title,
fontSize: 20,
textAlign: & # 39; center & # 39;
},
text: {
color: colors.text,
fontSize: 17,
textAlign: & # 39; center & # 39;
}
}
export the default palette

And then we can use them on our screen:

const styles = StyleSheet.create ({
container: {
flex: 1,
alignItems: & # 39; center & # 39;
},
heading: {... palette.heading, ... {
marginTop: 72
}}
})

Here we use the object spread operator to merge palette.heading and our personalized style object. This means that we use styles from palette.heading but also specify more properties.

If we had to go down the app for several brands, we could have more palettes. This is a really powerful model.

Generate images

You can see it inside /src/res/images.js we have the properties for each image in the src / res / images folder:

const images = {
button: require (& # 39; ./ images / button.png & # 39;),
logo: require (& # 39; ./ images / logo.png & # 39;),
placeholder: require (& # 39; ./ images / placeholder.png & # 39;)
}
export predefined images

This is boring to do manually, and we have to update ourselves if there are changes in the image naming convention. Instead, we can add a script to generate the images.js based on the images we have. Add a file to the root of the project /scripts/images.js:

const fs = require (& # 39; fs & # 39;)
const imageFileNames = () => {
const array = fs
.readdirSync (& # 39; src / RES / images & # 39;)
.filter ((file) => {
return file.endsWith (& # 39 ;. png & # 39;)
})
.map ((file) => {
return file.replace (& # 39;@ 2x.png & # 39;, & # 39; & # 39;) .replace (& # 39;@ 3x.png & # 39;, & # 39; & # 39;)
})
return Array.from (new Set (array))
}
const generate = () => {
let properties = imageFileNames ()
.map ((name) => {
return `$ {name}: require (& # 39; ./ images / $ {name} .png & # 39;)`
})
.join (& # 39 ;, n & # 39;)
const string = `const images = {
$ {} property
}
export predefined images
`
fs.writeFileSync (& # 39; src / res / images.js & # 39 ;, string, & # 39; utf8 & # 39;)
}
create()

The nice thing about Node is that we have access to the fs module, which is really good at processing files. Here we simply cross the images and update /src/res/images.js Consequently.

Whenever we add or change images, we can perform:

node / images.js script

And we can also declare the script inside our main package.json :

"script": {
"start": "node node_modules / react-native / local-cli / cli.js start",
"test": "jest",
"lint": "eslint * .js ** / *. js",
"images": "node scripts / images.js"
}

Now we can only run npm performs images and we get an update images.js resource file.

The namespace R

This passage depends on personal taste, but I find it more organized if we introduce the namespace R, just as Android does for resources with the generated R class.

Once you have outsourced the app resources, you can access them using the resource IDs generated in the projects Rclass. This document shows how to group resources in the Android project and provide alternative resources for specific device configurations, then access them from the app code or other XML files.

In this way, we make a file called R.js in src / library:

import strings from & # 39; ./ strings & # 39;
import images from & # 39; ./ images & # 39;
import colors from & # 39; ./ colors & # 39;
import the palette from & # 39; ./ palette & # 39;
const R = {
strings,
images,
colors,
palette
}
default export R

And access it on the screen:

imports R from & # 39; res / R & # 39;
render () {
return (

<Picture
style = {} styles.logo
source = {R.images.logo} />
<Picture
style = {} styles.image
source = {R.images.placeholder} />
{R.strings.onboarding.welcome.title.toUpperCase ()}
)
}

Replace strings with R.strings, colors with r.colors, is images with R.images. With the R annotation, it is clear that we are accessing the static resources from the app package.

This also corresponds to the Airbnb singleton convention, as our R is now a global constant.

23.8 Use PascalCase when exporting a constructor / class / singleton / function library / naked object.

const AirbnbStyleGuide = {
es6: {
},
}

export default AirbnbStyleGuide

Where to go from here

In this post, I showed you how I think you should structure folders and files in a React Native project. We have also learned how to manage resources and access them more securely. I hope you found it useful. Here are some other resources to explore further:




Source link

Leave a Reply

Your email address will not be published.