The demo address:https://github.com/recall-lidemin/baseapp.git
The demo is not perfect. The article is mainly written according to the project I am doing now. I will improve the demo sometime
1. Environment preparation
Before the start of the app development, we need to build the app development environment, including ios and android, the construction of the concrete steps can consult the official website of reactnative. Cn/docs/gettin…
All the third-party packages in the following are from the website js.coach/, and you can refer to the website for ios and Android configuration of all relevant packages. The specific configuration is not explained in this article, but you need to learn to read the documents when writing projects. This article is only a basic introduction, and the configuration of specific packages can follow the guidance and documentation.
If you use expo scaffolding to build your app, it will be much easier, it will help you integrate, but not as flexible as using native RN, some places expo may not work and some third party packages will not work
2. Initialize the project
Once you have configured your local environment, you can start your App project creation and development
2.1. Run the following command to initialize the project
npx react-native init MyApp --template react-native-template-typescript
- This command is initialized with the name
MyApp
Project, and use TS template, this TS must be added, this year, the project who also does not add a TS - Ts learning, search a video, a look on the line, interview, honest to memorize it, write projects, on the point of the real line
2.2. Configure multiple environments
- Use the react-native config package in the
https://js.coach/
Search through the website - Install the package
yarn add react-native-config
- Configure ios and Android based on the package document
- After the configuration, create it in the root directory of the project
.env
file.env.dev
file.env.test
file.env.prod
file - configuration
package.json
Start and build scripts
"scripts": {
"android": "cp -f .env.dev .env && react-native run-android"."ios": "cp -f .env.dev .env && react-native run-ios"."start": "cp -f .env.dev .env && react-native start"."build": "cp -f .env.prod .env && react-native start"."test": "cp -f .env.test .env && react-native start"."build:prod": "cp -f .env.prod .env && cd android && ./gradlew app:assembleRelease"."build:test": "cp -f .env.test .env && cd android && ./gradlew app:assembleRelease"."lint": "eslint . --ext .js,.jsx,.ts,.tsx"."lint-staged": "lint-staged"."lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx "."icon": "npx iconfont-rn"
},
Copy the code
cp -f .env.dev .env
This is to copy the configuration of different environments to.env
File to ensure that different commands are executed and different environment variables are distinguished- Use also refer to the documentation
2.3. Configure an absolute path
- Use the babel-plugin-module-resolver plug-in
- The installation
yarn add babel-plugin-module-resolver
- in
babel.config.js
Add the following code to the file
plugins: [
[
'module-resolver',
{
root: ['./src'].alias: {
The '@': './src'}}]]Copy the code
- in
tsconfig.json
In the configuration
"baseUrl": "./src"."paths": {
"@ / *": ["*"]}Copy the code
3. Configure the navigator
Use the react – navigation, is now at version 5.0, version 5.0 of previous package split, every navigator split into separate packages, specific use our document reference website https://reactnavigation.org/ can be configured
3.1. Install the navigator core package and dependency package
- The installation
yarn add @react-navigation/native
- Because this library relies on other libraries, native RN applications are instructed to install dependent libraries
yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
Copy the code
- From React Native 0.60 and higher, linking is automatic. So you don’t need to run react-native link. No need for us to manually link
- In index.js or app.tsx
import 'react-native-gesture-handler';
- The ios side,
Run CD ios && Pod Install
3.2. Install the stack navigator
yarn add @react-navigation/stack
- Create stack navigators in the page. Note that all navigators must be included in the core package deconstructed
NavigationContainer
In the container
import React, {FC} from 'react'
import {
CardStyleInterpolators,
createStackNavigator,
HeaderStyleInterpolators,
StackNavigationProp
} from '@react-navigation/stack'
import Detail from '@/pages/detail'
import {Platform, StyleSheet} from 'react-native'
import BottomTabs from './BottomStackNavigator'
export type RootStackParamList = {
BottomTabs: { screen? : string }Home: undefined
Detail: {id: number}
}
export type RootStackNavigation = StackNavigationProp<RootStackParamList>
/** * Create stack Navigator * Navigator route * Screen page */
const Stack = createStackNavigator<RootStackParamList>()
const RootNavigator: FC = () = > {
return (
<Stack.Navigator
headerMode="float"
screenOptions={{
headerStyleInterpolator: HeaderStyleInterpolators.forUIKit.cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS.gestureEnabled: true// Enable android gesturesgestureDirection: 'horizontal', // Gesture direction set levelheaderStyle: {
. Platform.select({
android:{// Android title bar styleelevation: 0.borderBottomWidth: StyleSheet.hairlineWidth}})},headerTitleAlign: 'center'
}}>
<Stack.Screen name="BottomTabs" component={BottomTabs} />// Create a nested tag navigator<Stack.Screen
options={{headerTitle:'details'}}name="Detail"
component={Detail}
/>
</Stack.Navigator>)}export default RootNavigator
Copy the code
3.3. Install label navigator
yarn add @react-navigation/bottom-tabs
- Create a label navigator
import React, {FC, useEffect} from 'react'
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs'
import Home from '@/pages/home'
import Mine from '@/pages/mine'
import {RouteProp, getFocusedRouteNameFromRoute} from '@react-navigation/native'
import {RootStackNavigation, RootStackParamList} from './RootStackNavigation'
export type BottomStackParamList = {
Home: undefined
Listen: undefined
Mine: undefined
}
type Route = RouteProp<RootStackParamList, 'BottomTabs'>
interface IProps {
navigation: RootStackNavigation
route: Route
}
const Tab = createBottomTabNavigator<BottomStackParamList>()
const getHeaderTitle = (route: Route) = > {
constrouteName = getFocusedRouteNameFromRoute(route) || route.params? .screen ||'Home'
switch (routeName) {
case 'Home':
return 'home'
case 'Mine':
return 'I'
default:
break}}const BottomTabs: FC<IProps> = ({navigation, route}) = > {
useEffect(() = > {
navigation.setOptions({
headerTitle: getHeaderTitle(route)
})
}, [navigation, route])
return (
<Tab.Navigator
tabBarOptions={{
activeTintColor: '#f86442'
}}>
<Tab.Screen
name="Home"
options={{
tabBarLabel:'Home'}}component={Home}
/>
<Tab.Screen
name="Mine"
options={{
tabBarLabel:'Mine'}}component={Mine}
/>
</Tab.Navigator>)}export default BottomTabs
Copy the code
3.4 Modal navigator
import React, {FC} from 'react'
import {
createStackNavigator,
StackNavigationProp,
TransitionPresets
} from '@react-navigation/stack'
import Login from '@/pages/login'
import RootNavigator from './RootStackNavigation'
export type ModalStackParamList = {
Login: undefined
RootNavigator: { screen? : string } }export type ModalStackNavigation = StackNavigationProp<ModalStackParamList>
const ModalStack = createStackNavigator<ModalStackParamList>()
const ModalStackNavigation: FC = () = > {
return (
<ModalStack.Navigator
mode="modal"
headerMode="screen"
screenOptions={{
headerTitleAlign: 'center',
gestureEnabled: true.. TransitionPresets.ModalSlideFromBottomIOS.headerBackTitleVisible: false.headerShown: false}} >
<ModalStack.Screen name="Login" component={Login} />
<ModalStack.Screen name="RootNavigator" component={RootNavigator} />// Nested root stack navigator</ModalStack.Navigator>)}export default ModalStackNavigation
Copy the code
3.5. Navigator nesting
- The core package is used here
NavigationContainer
Container, wrap up all the navigators,NavigationContainer
Just wrap one in the outermost layer
import React, {FC} from 'react'
import {NavigationContainer} from '@react-navigation/native'
import ModalStackNavigation from './ModalStackNavigation' // Introduce the Modal navigator above
const Navigator: FC = () = > {
return (
<NavigationContainer>
<ModalStackNavigation />
</NavigationContainer>)}export default Navigator
Copy the code
4. Introduce the dva
- The installation
yarn add dva-core-ts dva-loading-ts
- Start by creating a new model file, models/model.ts
import { Effect, Model } from 'dva-core-ts'
import { Platform } from 'react-native'
import { Reducer } from 'redux'interface CurrentUser {} interface UserState { currentUser? : CurrentUser } interface UserModalextends Model {
namespace: 'user'
state: UserState
reducers: {
saveCurrentUser: Reducer<UserState>
}
effects: {
fetch: Effect
getUser: Effect
clearUser: Effect
}
}
const userModal: UserModal = {
namespace: 'user'.state: {
currentUser: undefined
},
effects: {*fetch(_, { call, put }) {
// Request logic}},reducers: {
saveCurrentUser(state, action) {
return {
...state,
currentUser: action.payload
}
}
}
}
export default userModal
Copy the code
- Create the repository store/ dvA.ts
import { create } from 'dva-core-ts'
import createLoading from 'dva-loading-ts'
import models from '@/models/model'
// 1. Create a DVA instance
const app = create()
// 2. Load model objects
models.forEach((model) = > {
app.model(model)
})
app.use(createLoading())
// 3. Start dVA
app.start()
// 4. Export DVA data
export default app._store
Copy the code
- Registration, introduced in app.tsx, is registered to the top level and all of the following components can be consumed
/* eslint-disable radix */
import React, { useEffect, useRef, useState } from 'react'
import { Provider } from 'react-redux'
import store from '@/store/dva'
const App = () = > {
return (
<Provider store={store}>
<NavigatorBase />
</Provider>)}export default App
Copy the code
5. Picture selection and photo taking (support multiple selection)
yarn add react-native-image-crop-picker
6. Ajax requests
yarn add axios
7. Integrated codepush
- Microsoft provides app hot update, installation
react-native-code-push
Currently codePush is no longer supported and appCenter is officially recommended to replace it. However, there are still many codePush solutions online, so CODEPush is still used - For the react-native code-push integration solution, see git:
https://github.com/microsoft/react-native-code-push
The documentation is very detailed - Codepush account creation, APP registration, update push and other commands
https://docs.microsoft.com/en-us/appcenter/distribution/codepush/cli
- Modify the app. TSX file
/* eslint-disable radix */
import React, { useEffect, useRef, useState } from 'react'
import { Provider } from 'react-redux'
import store from '@/store/dva'
import 'react-native-gesture-handler'
import NavigatorBase from '@/navigator'
import { AppState, Platform, Text, View } from 'react-native'
import CodePush from 'react-native-code-push'
import { hp, wp } from '@/utils'
import * as Progress from 'react-native-progress'
import { primary_color } from '@/config'
import Config from 'react-native-config'
const App = () = > {
const [syncMessage, setSyncMessage] = useState<string>()
const [progress, setProgress] = useState<any>()
const codePushStatusDidChange = (syncStatus: any) = > {
switch (syncStatus) {
case CodePush.SyncStatus.CHECKING_FOR_UPDATE:
setSyncMessage('Check for updates... ')
break
case CodePush.SyncStatus.DOWNLOADING_PACKAGE:
setSyncMessage('Downloading update:')
break
case CodePush.SyncStatus.AWAITING_USER_ACTION:
setSyncMessage('Waiting')
break
case CodePush.SyncStatus.INSTALLING_UPDATE:
setSyncMessage('In installation')
break
case CodePush.SyncStatus.UP_TO_DATE:
setSyncMessage('Current version')
setProgress(false)
break
case CodePush.SyncStatus.UPDATE_IGNORED:
setSyncMessage('Update cancelled')
setProgress(false)
break
case CodePush.SyncStatus.UPDATE_INSTALLED:
setSyncMessage('Restart to apply the update')
setProgress(false)
CodePush.allowRestart()
CodePush.restartApp()
break
case CodePush.SyncStatus.UNKNOWN_ERROR:
setSyncMessage('Unknown error')
setProgress(false)
break}}const codePushDownloadDidProgress = (pro: any) = > {
setProgress(pro)
}
// code-push
const syncImmediate = () = > {
CodePush.sync(
{
// Install mode
//ON_NEXT_RESUME the next time it returns to the foreground
//ON_NEXT_RESTART The next restart
//IMMEDIATE updates immediately
mandatoryInstallMode: CodePush.InstallMode.IMMEDIATE, ... Platform.select({ios: {
deploymentKey: Config.IOS_KEY
},
android: {
deploymentKey: Config.ANDROID_KEY
}
}),
/ / dialog
updateDialog: {
// Whether to display the update description
appendReleaseDescription: true.// Update the description prefix. The default is "Description"
descriptionPrefix: 'Update:'.// Forces the button text to be updated. The default is continue
mandatoryContinueButtonLabel: 'Update now'.// Mandatory update information. The default is "An update is available that must be installed."
mandatoryUpdateMessage: 'Must be updated before use'.// The text of the button is "ignore" by default.
optionalIgnoreButtonLabel: 'ignore'.// Confirm button text when not forced to update. The default is "Install"
optionalInstallButtonLabel: 'Update now'.// The updated message text is checked when the update is not mandatory
optionalUpdateMessage: ' '.// Title of the Alert window
title: 'Version Update'
}
},
codePushStatusDidChange,
codePushDownloadDidProgress
)
}
CodePush.disallowRestart() // Disable restart
syncImmediate() // Start checking for updates
let progressView
if (progress) {
progressView = (
<>
<View
style={{
position: 'absolute',
left: wp(5),
top: hp(25),
width: wp(90),
height: 80.backgroundColor: '#eee',
zIndex: 10.alignItems: 'center',
borderRadius: 5}} >
<Text style={{ marginVertical: 15.fontSize: 16}} >Version update</Text>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center'
}}>
<Text
style={{
fontSize: 16.color: '#333',
alignItems: 'center',
justifyContent: 'center'
}}>
{syncMessage}
</Text>
<Progress.Bar
color={primary_color}
progress={progress.receivedBytes / progress.totalBytes}
width={wp(50)}
height={16}
borderRadius={8}
/>
<Text style={{ marginHorizontal: 5}} >
{parseInt(
(
(progress.receivedBytes / progress.totalBytes) *
100
).toString()
)}
%
</Text>
</View>
</View>
</>)}return (
<Provider store={store}>
<NavigatorBase />
{progressView}
</Provider>)}let codePushOptions = {
// Set the frequency of checking for updates
// when the ON_APP_RESUME APP returns to the foreground
//ON_APP_START when the APP is started
//MANUAL MANUAL check
checkFrequency: CodePush.CheckFrequency.ON_APP_RESUME
}
export default CodePush(codePushOptions)(App)
Copy the code