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,
    }
    
    ```
    
    ```js
    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](https://ik.imagekit.io/leiakito/Actual%20Combat/React%20Native%20%E5%AE%9E%E6%88%98%E7%B3%BB%E5%88%97.webp?updatedAt=1739878232546)
    
    `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
    
    ```sh
    expo install @react-native-picker/picker
    
    ```
    
    We can then implement a selector using `Picker`. `Select`
    
    ```js
    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`
    
    ```js
    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`
    
    ```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](https://ik.imagekit.io/leiakito/Actual%20Combat/React%20Native%20%E5%AE%9E%E6%88%98%E7%B3%BB%E5%88%97%20(1).webp?updatedAt=1739878232354)
    
    # 1.3. Encapsulate a Toggling
    
    ```js
    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>
      )
    }
    
    ```
    
    ```js
    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
    
    ```js
    expo install @react-native-community/datetimepicker
    
    ```
    
    Then we develop a `DateTimePicker` component for `ios`
    
    ```js
    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](https://ik.imagekit.io/leiakito/Actual%20Combat/React%20Native%20%E5%AE%9E%E6%88%98%E7%B3%BB%E5%88%97%20(2).webp?updatedAt=1739878232504)
    
    Android will be a little more troublesome
    
    ```js
    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](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.webp?updatedAt=1739878231929)
    
    # 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.
    
    ```js
    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`
    
    ```js
    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`
    
    ```javascript
    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`
    
    ```javascript
    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.
    
    ```javascript
    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`
    
    ```javascript
    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](https://juejin.cn/user/400646714977431/pins) 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.