Detailed introduction to using the new version of react-native 0.71 to quickly develop APP projects (ts+zustand+react-navigation+react-native-vector-icons)

1. Project introduction

Using the new version of react-native 0.71 to quickly develop the APP, the login, home page, settings and some demo modules were initially implemented, and some common components were customized. It includes simple examples of routing components, text box components, cell components, button components, table components and pop-up window selection components. These components are the simplest implementations and are for reference by novice friends. Later, it can be expanded according to its own needs.

This project uses the relatively new version of the rn framework (0.71) and is developed using the ts framework. The state management library does not use react reducer, but uses Zustand: a lightweight, modern state management library (similar to Pinia in Vue).

 

2. Create a project

 Create a react-native project and install the dependent libraries we need

1.npx react-native init birckdogApp --version 0.71.0
2.npm install zustand 
3.npm install @react-navigation/native 
4.npm install @react-navigation/native-stack
5.npm install @react-navigation/bottom-tabs
6.npm install react-native-screens react-native-safe-area-context 
7.npm install @react-native-async-storage/async-storage
8.npm install react-native-vector-icons --save
9.npm i --save-dev @types/react-native-vector-icons

2.1 I need to change the maven that each project depends on to the Alibaba Cloud warehouse.

 2.2 The react-native-vector-icons library needs to be referenced in the build.gradle file of the main project

apply from: file("../../node_modules/react-native-vector-icons/fonts.gradle");

3. Login page

 3.1 Use zustand to build a simple UserInfo state management object

Use type to define the UserInfo type and UserStoreInfo type, and use zustand's create to create a useUserInfo state management object.

useUserInfo The user object in state management is used to store user data.

useUserInfo The setUserInfos method in state management assigns a value to the user object. (user: UserInfo | null) => Promise<void> means that the method parameter is user, the parameter type is UserInfo and the parameter can be empty. The method return type is a void Promise object. .

The userInitialize method in useUserInfo state management loads the data stored in AsyncStorage into the user object. Because the operation of AsyncStorage is an asynchronous operation, async must be added in front of the method. In our method, we will load user information when the main app program starts, because all state management objects in zustand will be lost after the app is closed.

import { create } from 'zustand';
import AsyncStorage from '@react-native-async-storage/async-storage';

type UserInfo = {
  id: string;
  account: string;
  userName: string;
  token: string;
};

type UserStoreInfo = {
  user: UserInfo | null;
  setUserInfos: (user: UserInfo | null) => Promise<void>;
  userInitialize: () => Promise<void>;
};

const useUserInfo = create<UserStoreInfo>((set) => ({
  user: null,
  setUserInfos: (user) => {
    return new  Promise<void>(async resolve =>  {
      try {
        // 将用户信息保存到本地存储
        await AsyncStorage.setItem('user', JSON.stringify(user));
        set({ user });
      } catch (error) {
        console.error('Failed to set user in AsyncStorage:', error);
      }
      resolve();
    });
  },
  userInitialize: async () => {
    return new  Promise<void>(async resolve =>  {
      try {
        // 从本地存储中获取用户信息
        const user = await AsyncStorage.getItem('user') as string;
        set({ user: JSON.parse(user) });
      } catch (error) {
        console.error('Failed to get user from AsyncStorage:', error);
      }
      resolve();
    });
  },
}));

export default useUserInfo;

3.2 Create a login page

We use our own defined BdButton component for the login page. Using our own encapsulated components can better unify the UI style and simplify the code. We will explain how to customize components later.

import React, { useState } from 'react';
import { View, TextInput, StyleSheet } from 'react-native';
import useUserInfo from '../../../src/stores/userInfo';
import BdButton from '../../../src/components/bd-Button';

const LoginScreen = () => {
  const [account, setAccount] = useState('');
  const [password, setPassword] = useState('');
  const { setUserInfos } = useUserInfo();
  const handleLoginUser = () => {
    const newUser = {
      id: Date.now().toString(),
      account,
      userName:'测试',
      token: Date.now().toString(),
    };
    setUserInfos(newUser);
    setAccount('');
    setPassword('');
  };

  return (
    <View style={styles.container}>
      <View style={styles.loginContainer}>
        <TextInput
          style={styles.input}
          placeholder="账号"
          value={account}
          onChangeText={setAccount}
        />
        <TextInput
          style={styles.input}
          placeholder="密码"
          value={password}
          onChangeText={setPassword}
        />
        <BdButton
          style={styles.button}
          text="登录"
          onPress={handleLoginUser}
        />
      </View>
    </View>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F7F8FA',
  },
  loginContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    marginBottom:200,
  },
  input: {
    width: '90%',
    height: 40,
    borderWidth: 1,
    borderColor: '#646566',
    marginBottom: 10,
    paddingHorizontal: 10,
  },
  button: {
    width: '90%',
  },
});
export default LoginScreen;

3.3 Program entry judgment login logic

App.tsx is the main entrance of our program. Here we use the useEffect hook to initialize user data when the app starts.

StatusBar is a component that controls the application status bar. Here, the background of the top status bar on the phone is set to light gray, and the font style in the status bar is set to dark mode.

Navigator is our page route.

import React, { useState, useEffect } from 'react';
import { StatusBar } from 'react-native';
import useUserInfo from '../src/stores/userInfo';
import LoginScreen from '../src/views/login/index';
import Navigator from '../src/components/navigator';
import { BdStyleConfig } from '../src/theme/bd-styles';

const App = () => {
  const { user, userInitialize } = useUserInfo();
  const [isLoad, setIsLoad] = useState(true);
  useEffect(() => {
    userInitialize().then(()=>{
      setIsLoad(false);
    });
  },[userInitialize]);

  return (
    <>
     <StatusBar backgroundColor={BdStyleConfig.color_lightGrey} barStyle='dark-content' />
    { isLoad ? '' : (user?.token ? <Navigator /> : <LoginScreen />) }
    </>
  );
};
export default App;

4. Build routing

Use @react-navigation/bottom-tabs to build the bottom tab of the main program (homepage and settings)

Use @react-navigation/native-stack to build the internal routing of each tab page (the homepage has sub-routing pages such as material shelving and warehouse receipt)

import React from 'react';
import Ionicons from 'react-native-vector-icons/Ionicons';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import MyHomeScreen from '../../src/views/myHome/index';
import SettingScreen from '../../src/views/setting/index';
import MaterialListingScreen from '../../src/views/business//materialListing/index';
import WarehouseReceiptScreen from '../../src/views/business//warehouseReceipt/index';
import MaterialSearchScreen from '../../src/views/business//warehouseReceipt/materialSearch';
import PrintSettingScreen from '../../src/views/business//printSetting/index';
import { BdStyles, BdStyleConfig } from '../../src/theme/bd-styles';

const Tab = createBottomTabNavigator();

function HomeStack() {
  return (
    <NavigationContainer>
      <Tab.Navigator
        screenOptions = {({ route }) => ({
          tabBarIcon: ({ focused, color, size }) => {
            let iconName = '';
            
            if (route.name === 'Home') {
              iconName = focused ? 'home' : 'home-outline';
            } else if (route.name === 'Setting') {
              iconName = focused ? 'settings' : 'settings-outline';
            };
            
            return <Ionicons name={iconName} size={size} color={color} />;
          },

          tabBarActiveTintColor: BdStyleConfig.color_primary,
          tabBarInactiveTintColor: 'gray',
        })}
      >
        <Tab.Screen name="Home"  options={
   
   { headerShown:false, title:'首页' }} component={MyHomeStack} />
        <Tab.Screen name="Setting" options={
   
   { headerShown:false, title:'设置' }} component={SettingScreen} />
      </Tab.Navigator>
    </NavigationContainer>
  );
}

const Stack = createNativeStackNavigator();

function MyHomeStack() {
  return (
   <Stack.Navigator screenOptions={
   
   {
      headerStyle: {
        backgroundColor: BdStyleConfig.color_lightGrey,
      },
      headerTintColor: '#000',
      headerTitleStyle: {
       color:'#000',
       fontSize:18,
      },
      headerTitleAlign: 'center', // 将标题居中
      animation: 'slide_from_right', // 通过animation属性设置动画效果
  }}>
    <Stack.Screen name="MyHome" options={
   
   { title:'首页' }}  component={MyHomeScreen} />
    <Stack.Screen name="MaterialListing" options={
   
   { title:'物料上架' }}  component={MaterialListingScreen} />
    <Stack.Screen name="WarehouseReceipt" options={
   
   { title:'仓库收料' }} component={WarehouseReceiptScreen} />
    <Stack.Screen name="MaterialSearch" options={
   
   { title:'收料查询' }} component={MaterialSearchScreen} />
    <Stack.Screen name="PrintSetting" options={
   
   { title:'打印设置' }} component={PrintSettingScreen} />
  </Stack.Navigator>
  );
}

export default function Navigator() {
    return (
        <HomeStack />
    );
}

5. Create a home page

import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
import Ionicons from 'react-native-vector-icons/Ionicons';
import { NavigationProp, ParamListBase } from '@react-navigation/native';
import { BdStyles, BdStyleConfig } from '../../../src/theme/bd-styles';

const MyHomeScreen = ({ navigation }: { navigation: NavigationProp<ParamListBase> }) => {
  const handleMenuPress = (menu:string) => {
    navigation.navigate(menu);
  };
  return (
    <View style={BdStyles.container}>
      <View style={styles.row}>
        <TouchableOpacity
          style={[styles.menuItem, styles.menuBorder]}
          onPress={() => handleMenuPress('WarehouseReceipt')}
        >
          <Ionicons name="archive-outline" size={30} color="#646566" />
          <Text style={styles.menuTitle}>仓库收料</Text>
        </TouchableOpacity>
        <TouchableOpacity
          style={[styles.menuItem, styles.menuBorder]}
          onPress={() => handleMenuPress('MaterialListing')}
        >
          <Ionicons name="arrow-up-circle-outline" size={30} color="#646566" />
          <Text style={styles.menuTitle}>物料上架</Text>
        </TouchableOpacity>
        <TouchableOpacity
          style={[styles.menuItem, styles.menuBorder]}
        >
          <Ionicons name="library-outline" size={30} color="#646566" />
          <Text style={styles.menuTitle}>工单拣料</Text>
        </TouchableOpacity>
      </View>
      /**其他菜单**/
  </View>
  );
};

const styles = StyleSheet.create({
  row: {
    flexDirection: 'row',
  },
  menuItem: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    paddingBottom:15,
    paddingTop:15,
    backgroundColor:BdStyleConfig.color_white,
  },
  menuBorder: {
    borderColor: '#ebedf0',
    borderWidth: 0.5,
  },
  menuTitle: {
    color: '#646566',
    marginTop: 5,
  },
});

export default MyHomeScreen;

6. Create settings

Here we use the custom cell component BdCell

import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Alert } from 'react-native';
import useUserInfo from '../../../src/stores/userInfo';
import Ionicons from 'react-native-vector-icons/Ionicons';
import BdCell from '../../components/bd-cell';

const SettingScreen = () => {
  const { user, setUserInfos } = useUserInfo();

  const handleLoginOut = () => {
    Alert.alert(
      "消息",
      "是否确定退出登录!",
      [
        {
          text: "取消",
          onPress: () => console.log("Cancel Pressed"),
          style: "cancel"
        },
        { text: "确定", onPress: () =>  setUserInfos(null) }
      ]
    );
  };
  return (
    <View style={styles.container}>
      <TouchableOpacity style={styles.userInfoContainer}>
          <Ionicons name="person-circle" size={80} color="#c1c1c1" />
          <View style={
   
   { alignItems: 'center', justifyContent: 'center',}}>
            <Text style={
   
   { fontSize:25, color:'#000', fontWeight: 'bold', marginBottom:5 }}>{ user?.userName }</Text>
            <Text style={
   
   { fontSize:13, color:'#c1c1c1',paddingLeft:7 }}>欢迎您!</Text>
          </View>
      </TouchableOpacity>
      <View style={styles.listContainer}>
        <BdCell title="系统设置" style={styles.cellStyle} />
        <BdCell title="退出" onPress={handleLoginOut} />
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor:'#F7F8FA',
  },
  userInfoContainer: {
    flexDirection: 'row',
    backgroundColor:'#fff',
    padding:10,
  },
  listContainer: {
    paddingTop:10,
  },
  cellStyle:{
    borderBottomColor:'#F7F8FA',
    borderBottomWidth:1,
  }
});

export default SettingScreen;

7. Custom component development

7.1 Button component BdButton

To customize the BdButton button component, we first define how many properties the ButtonProps type contains for the component. The style attribute allows us to rewrite the component style. The ? in style? is an optional connection operator. The definition here means that this parameter can be empty or not. pass.

import React from 'react';
import { TouchableOpacity, Text, StyleSheet, ViewStyle } from 'react-native';
import { BdStyles, BdStyleConfig } from '../../src/theme/bd-styles';

interface ButtonProps {
    style?: ViewStyle;
    onPress?: () => void;
    text: string;
}

const BdButton: React.FC<ButtonProps> = ({ style, onPress, text }) => {
  return (
    <TouchableOpacity style={[styles.button, style]} onPress={onPress}>
      <Text style={styles.text}>{text}</Text>
    </TouchableOpacity>
  );
};

const styles = StyleSheet.create({
  button: {
    backgroundColor:BdStyleConfig.color_primary,
    padding: 10,
    borderRadius: 5,
    alignItems: 'center',
    justifyContent: 'center',
  },
  text: {
    color: 'white',
    fontWeight: 'bold',
  },
});

export default BdButton;

7.2 Table component BdTable

The simple use of the table component, we only need to pass the data and columns attributes to the component to generate our table.

const tableData = [
  { id: '1001', count: 5, total: 15, remainCount: 3 },
  { id: '1002', count: 7, total: 17, remainCount: 2 },
  { id: '1003', count: 3, total: 12, remainCount: 1 },
  // 其他数据...
]

const tableColumns:BdTablePropsColumn[] = [
  { field: 'id',  title: '料号', align: 'center', },
  { field: 'count',  title: '件数', align: 'center', },
  { field: 'total',  title: '总数量', align: 'center', },
  { field: 'remainCount',  title: '剩余数量', align: 'center', },
]

const MaterialSearchScreen = () => {
  return (
    <View style={BdStyles.container}>
      <BdTable
        data={tableData}
        columns={tableColumns}
      />
    </View>
  );
};

Table component code

import React from 'react';
import { View, Text, VirtualizedList, StyleSheet, TouchableOpacity } from 'react-native';

export interface BdTableProps {
  data: any[];
  columns: BdTablePropsColumn[];
  onClickRow?: ((item:any) => void) | undefined;
  hideHeader?: boolean;
}

export interface BdTablePropsColumn {
  field: string,
  title: string,
  align: string,
}

const BdTable: React.FC<BdTableProps> = ({ columns, data, onClickRow, hideHeader }) => {
  const renderItem = (item:any) => (
    <TouchableOpacity key={new Date().getTime()} style={styles.row} onPress={() =>{ onClickRow?onClickRow(item.item):'' }}>
      {columns.map((column) => (
        <Text style={styles.cell}>{item.item[column.field]}</Text>
      ))}
    </TouchableOpacity>
  );

  const tableHeader = () => (
    <View style={styles.row}>
      {columns.map((column) => (
         <Text key={column.field}  style={styles.headerCell}>{column.title}</Text>
      ))}
    </View>
  );

  const keyExtractor = (item:any) => item.id?item.id.toString():new Date().getTime();
  const getItemCount = () => data.length;
  const getItem = (data: [], index: number) => data[index];
  return (
    <VirtualizedList
      data={data}
      renderItem={renderItem}
      keyExtractor={keyExtractor}
      ListHeaderComponent={ !hideHeader?tableHeader:undefined }
      getItemCount={getItemCount}
      getItem={getItem}
    />
  );
};
const styles = StyleSheet.create({
  row: {
    flexDirection: 'row',
    justifyContent: 'space-between',

    backgroundColor:'#fff',
  },
  cell: {
    flex: 1,
    textAlign: 'center',
    borderColor: '#e4eaec',
    borderWidth:1,
    padding:4,
  },
  headerCell: {
    flex: 1,
    textAlign: 'center',
    borderColor: '#e4eaec',
    borderWidth:1,
    fontWeight:'bold',
    padding:4,
  },
});

export default BdTable;

8.Project structure

Guess you like

Origin blog.csdn.net/qq243348167/article/details/131795062