React Native

    React-native practical series

    Published
    April 21, 2023
    Reading Time
    6 min read
    Author
    Felix
    Access
    Public

    1. Use of components

    The mobile terminal also has forms, and forms are also a scene I often come into contact with, so let's implement some form components.

    1.1. Encapsulate an input

    Let’s start with the TextInput input box of rn

    input

    import React, { useState } from 'react'
    import PropTypes from 'prop-types'
    import { Text, TextInput, View } from 'react-native'
    import styles from './styles'
    function Input(props) {
      return (
        <View style={styles.textInputContainer}>
          <Text style={styles.textInputLabel}>{props.label}</Text>
          <TextInput style={styles.textInput} {...props} />
        </View>
      )
    }
    Input.propTypes = {
      label: PropTypes.string,
    }
    
    
    export default function CollectingTextInput() {
      const [changedText, setChangedText] = useState('')
      const [submittedText, setSubmittedText] = useState('')
      return (
        <View style={styles.container}>
          <Input label="Basic Text Input:" />
          <Input label="Password Input:" secureTextEntry />
          <Input label="Return Key:" returnKeyType="search" />
          <Input label="Placeholder Text:" placeholder="Search" />
          <Input
            label="Input Events:"
            onChangeText={e => {
              setChangedText(e)
            }}
            onSubmitEditing={e => {
              setSubmittedText(e.nativeEvent.text)
            }}
            onFocus={() => {
              setChangedText('')
              setSubmittedText('')
            }}
          />
          <Text>Changed: {changedText}</Text>
          <Text>Submitted: {submittedText}</Text>
        </View>
      )
    }
    
    
    

    Let's first see what these input boxes look like on the screen.

    image.png

    secureTextEntry password input disk, your input will be blocked by ***.

    Return key, this value determines the value of the lower right corner of your keypad.

    ![image.png](https://ik.imagekit.io/leiakito/Actual%20Combat/3d03e17cc9aa4a9790fc24a dcd73510b~tplv-k3u1fbpfcp-zoom-in-crop-mark_1512_0_0_0.webp?updatedAt=1739878232497)

    We can also use keyboardType to control the pop-up keyboard type, such as numeric

    ![image.png](https://ik.imagekit.io/leiakito/Actual%20Combat/78ac56c393e84ec29e81b5b 8a844f9bc~tplv-k3u1fbpfcp-zoom-in-crop-mark_1512_0_0_0.webp?updatedAt=1739878232305)

    1.2. Encapsulate a select

    first

    expo install @react-native-picker/picker
    
    

    We can then implement a selector using Picker. Select

    import { View, Text } from 'react-native'
    import { Picker } from '@react-native-picker/picker'
    import styles from './styles'
    export default function Select(props) {
      return (
        <View>
          <Text style={styles.pickerLabel}>{props.label}</Text>
          <Picker {...props}>
            {props.items.map(i => (
              <Picker.Item key={i.label} {...i} />
            ))}
          </Picker>
        </View>
      )
    }
    
    

    style

    import { StyleSheet } from 'react-native'
    export default StyleSheet.create({
      container: {
        flex: 1,
        flexDirection: 'column',
        backgroundColor: 'ghostwhite',
        justifyContent: 'center',
      },
      pickersBlock: {
        flex: 2,
        flexDirection: 'row',
        justifyContent: 'space-around',
        alignItems: 'center',
      },
      pickerHeight: {
        height: 250,
      },
      pickerContainer: {
        flex: 1,
        flexDirection: 'column',
        alignItems: 'center',
        backgroundColor: 'white',
        padding: 6,
        height: 240,
      },
      pickerLabel: {
        fontSize: 14,
        fontWeight: 'bold',
      },
      picker: {
        width: 150,
        backgroundColor: 'white',
      },
      selection: {
        flex: 1,
        textAlign: 'center',
      },
    })
    
    
    

    app.js

    import React, { useState } from "react";
    import { View, Text } from "react-native";
    import styles from "./styles";
    import Select from "./Select";
    
    const sizes = [
      { label: "", value: null },
      { label: "S", value: "S" },
      { label: "M", value: "M" },
      { label: "L", value: "L" },
      { label: "XL", value: "XL" },
    ];
    
    const garments = [
      { label: "", value: null, sizes: ["S", "M", "L", "XL"] },
      { label: "Socks", value: 1, sizes: ["S", "L"] },
      { label: "Shirt", value: 2, sizes: ["M", "XL"] },
      { label: "Pants", value: 3, sizes: ["S", "L"] },
      { label: "Hat", value: 4, sizes: ["M", "XL"] },
    ];
    
    export default function SelectingOptions() {
      const [availableGarments, setAvailableGarments] = useState([]);
      const [selectedSize, setSelectedSize] = useState(null);
      const [selectedGarment, setSelectedGarment] = useState(null);
      const [selection, setSelection] = useState("");
    
      return (
        <View style={styles.container}>
          <View style={styles.pickersBlock}>
            <Select
              label="Size"
              items={sizes}
              selectedValue={selectedSize}
              onValueChange={(size) => {
                setSelectedSize(size);
                setSelectedGarment(null);
                setAvailableGarments(
                  garments.filter((i) => i.sizes.includes(size))
                );
              }}
            />
            <Select
              label="Garment"
              items={availableGarments}
              selectedValue={selectedGarment}
              onValueChange={(garment) => {
                setSelectedGarment(garment);
                setSelection(
                  `${selectedSize} ${
                    garments.find((i) => i.value === garment).label
                  }`
                );
              }}
            />
          </View>
          <Text style={styles.selection}>{selection}</Text>
        </View>
      );
    }
    
    

    The final effect is as follows:

    image.png

    1.3. Encapsulate a Toggling

    import { View, Text, Switch } from 'react-native'
    import styles from './styles'
    export default function CustomSwitch(props) {
      return (
        <View style={styles.customSwitch}>
          <Text>{props.label}</Text>
          <Switch {...props} />
        </View>
      )
    }
    
    
    import React, { useState } from 'react'
    import { View } from 'react-native'
    import styles from './styles'
    import Switch from './Switch'
    export default function TogglingOnAndOff() {
      const [first, setFirst] = useState(false)
      const [second, setSecond] = useState(false)
      return (
        <View style={styles.container}>
          <Switch label="Disable Next Switch" value={first} disabled={second} onValueChange={setFirst} />
          <Switch label="Disable Previous Switch" value={second} disabled={first} onValueChange={setSecond} />
        </View>
      )
    }
    
    

    The effect is as follows:

    ![image.png](https://ik.imagekit.io/leiakito/Actual%20Combat/319e922f8adb4c71a2e20ef ef46b1673~tplv-k3u1fbpfcp-zoom-in-crop-mark_1512_0_0_0.webp?updatedAt=1739878232523)

    1.3. Implement a DateTimePicker

    First download the dependencies

    expo install @react-native-community/datetimepicker
    
    

    Then we develop a DateTimePicker component for ios

    import React from 'react'
    import { Text, View } from 'react-native'
    import DateTimePicker from '@react-native-community/datetimepicker'
    import styles from './styles'
    export default function DatePicker(props) {
      return (
        <View style={styles.datePickerContainer}>
          <Text style={styles.datePickerLabel}>{props.label}</Text>
          <DateTimePicker mode="date" display="spinner" {...props} />
        </View>
      )
    }
    
    
    

    Performance on ios

    image.png

    Android will be a little more troublesome

    import { Text, View } from 'react-native'
    import DateTimePicker from '@react-native-community/datetimepicker'
    import styles from './styles'
    function pickDate(options, onDateChange) {
      DateTimePicker.open(options).then(date => onDateChange(new Date(date.year, date.month, date.day)))
    }
    export default function DatePicker({ label, date, onDateChange }) {
      return (
        <View style={styles.datePickerContainer}>
          <Text style={styles.datePickerLabel}>{label}</Text>
          <Text onPress={() => pickDate({ date }, onDateChange)}>{date.toLocaleDateString()}</Text>
        </View>
      )
    }
    
    
    

    Performance on Android

    image.png

    2. How to implement a lazy loading list

    Suppose there is a requirement that requires us to implement a lazy loading list, which can also filter, sort, and pull-down refresh.

    First we need to manually define a data source. We can use iterator to implement a wirelessly incremented promise to simulate the api call. When fetchItems is called, our cnt will increase by 30.

    function* genItems() {
      let cnt = 0
    
      while (true) {
        yield `Item ${cnt++}`
      }
    }
    
    const items = genItems()
    
    export function fetchItems() {
      return Promise.resolve({
        json: () =>
          Promise.resolve({
            items: new Array(30).fill(null).map(() => items.next().value),
          }),
      })
    }
    
    

    After we define the data source, let's draw an overall component diagram.

    ![image.png](https://ik.imagekit.io/leiakito/Actual%20Combat/React%20Native%20%E5%AE% 9E%E6%88%98%20%E7%BB%84%E4%BB%B6%E4%BD%BF%E7%94%A8%20(1).webp?updatedAt=1739878232807)

    Let's first take a look at what components I have

    ![image.png](https://ik.imagekit.io/leiakito/Actual%20Combat/React%20Native%20%E5%AE% 9E%E6%88%98%20%E7%BB%84%E4%BB%B6%E4%BD%BF%E7%94%A8%20(2).webp?updatedAt=1739878232830)

    This is the final effect we want

    ![image.png](https://ik.imagekit.io/leiakito/Actual%20Combat/04a21ff6c19544f4a71c2af 6a643eb20~tplv-k3u1fbpfcp-zoom-in-crop-mark_1512_0_0_0.webp?updatedAt=1739878232177)

    Ok, theoretically speaking, when writing components, we should follow the data flow and write from top to bottom. But because bottom-up is easier for everyone to understand, we will write from the inside to the outside.

    Let's start with FlatList, which should be unfamiliar to everyone. It is a built-in component of rn, so what api are needed to implement our functions.

    • The first is lazy loading. We need to use onEndReached. If we do not specify the distance, it will be triggered when the distance to the bottom of the content is half the visible length of the current list, accepting a function.

    • To implement pull-down refresh, we use the onRefresh api and we need to pass a callback function into it.

    • The data source is specified by data.

    • And ListHeaderComponent is a header.

    When we already know what api is needed, we can try to write a most basic list component

    List

    import PropTypes from 'prop-types'
    import { Text, FlatList } from 'react-native'
    import ListControls from './ListControls'
    import styles from './styles'
    
    export default function List({ data, fetchItems, refreshItems, isRefreshing, onFilter, onSort, asc }) {
      return (
        <FlatList
          data={data}
          renderItem={({ item }) => <Text style={styles.item}>{item.value}</Text>}
          ListHeaderComponent={<ListControls {...{ onFilter, onSort, asc }} />}
          onEndReached={fetchItems}
          onRefresh={refreshItems}
          refreshing={isRefreshing}
        />
      )
    }
    
    List.propTypes = {
      fetchItems: PropTypes.func.isRequired,
      refreshItems: PropTypes.func.isRequired,
      isRefreshing: PropTypes.bool.isRequired,
    }
    
    
    

    Next we need to implement ListControls and ListContainer.

    ListControls

    import PropTypes from 'prop-types'
    import { View } from 'react-native'
    import styles from './styles'
    import ListFilter from './ListFilter'
    import ListSort from './ListSort'
    
    export default function ListControls({ onFilter, onSort, asc }) {
      return (
        <View style={styles.controls}>
          <ListFilter onFilter={onFilter} />
          <ListSort onSort={onSort} asc={asc} />
        </View>
      )
    }
    
    ListControls.propTypes = {
      onFilter: PropTypes.func.isRequired,
      onSort: PropTypes.func.isRequired,
      asc: PropTypes.bool.isRequired,
    }
    
    
    

    ListSort

    import PropTypes from 'prop-types'
    import { Text } from 'react-native'
    
    const arrows = new Map([
      [true, '▼'],
      [false, '▲'],
    ])
    
    export default function ListSort({ onSort, asc }) {
      return <Text onPress={onSort}>{arrows.get(asc)}</Text>
    }
    
    ListSort.propTypes = {
      onSort: PropTypes.func.isRequired,
      asc: PropTypes.bool.isRequired,
    }
    
    

    ListFilter, here autoFocus is to automatically focus and bring up the small keyboard when entering.

    import PropTypes from 'prop-types'
    import { View, TextInput } from 'react-native'
    import styles from './styles'
    
    export default function ListFilter({ onFilter }) {
      return (
        <View>
          <TextInput autoFocus placeholder="Search" style={styles.filter} onChangeText={onFilter} />
        </View>
      )
    }
    
    ListFilter.propTypes = {
      onFilter: PropTypes.func.isRequired,
    }
    
    
    

    You can see that there is not much logic code in these three parts, they are all codes for display. I put all the logic code in container.

    container, at this time think about what status we need to provide in the outer container.

    • A state asc that controls sorting

    • A state isRefreshing that controls loading

    • A data source pdata

    • A state filter that controls the search filter box

    Then what logic do we need to deal with

    • Search and sort the list display fetchItem -> data -> filterAndSort(data) -> pdata=useMemo(data)

    • Each time the data is pulled down, fetchItems is obtained

    • Pull new data after refreshing refreshItems

    So here we have completed a list of lazy loading, search, sort, and pull-down refresh

    import React, { useState, useEffect, useMemo } from 'react'
    import * as api from './api'
    import List from './List'
    
    const filterAndSort = (text, asc, array) => {
      if (array.length) {
        return array
          .filter(i => text.length === 0 || i.value.includes(text))
          .sort(asc ? (a, b) => a.index - b.index : (a, b) => b.index - a.index)
      }
    }
    
    const ListContainer = () => {
      const [data, setData] = useState([])
      const [isRefreshing, setIsRefreshing] = useState(false)
      const [asc, setAsc] = useState(true)
      const [filter, setFilter] = useState('')
      function fetchItems() {
        return api
          .fetchItems()
          .then(resp => resp.json())
          .then(({ items }) => {
            setData([...data, ...items])
          })
      }
    
      //My refresh simulates the process of pulling new data.
      function refreshItems() {
        setIsRefreshing(true)
        return api
          .fetchItems({ refresh: true })
          .then(resp => resp.json())
          .then(({ items }) => {
            setData(items)
          })
          .finally(() => {
            setIsRefreshing(false)
          })
      }
    
      useEffect(() => {
        fetchItems()
      }, [])
    
      const pdata = useMemo(() => {
        return filterAndSort(filter, asc, data)
      }, [filter, asc, data])
    
      return (
        <List
          data={pdata}
          fetchItems={fetchItems}
          refreshItems={refreshItems}
          isRefreshing={isRefreshing}
          asc={asc}
          onFilter={text => {
            setFilter(text)
          }}
          onSort={() => {
            setAsc(!asc)
          }}
        />
      )
    }
    
    export default ListContainer
    
    
    

    3. End

    At this point we have learned about the two most basic scenario forms and lists. If you have any questions or want to learn something more in-depth, just go to Boiling Point and add me. The free class is to let everyone learn the cross-terminal framework.

    Comments

    Join the conversation

    0 comments
    Sign in to comment

    No comments yet. Be the first to add one.