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.

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.

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

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:
.webp?updatedAt=1739878232354)
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:

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
.webp?updatedAt=1739878232504)
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

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.
.webp?updatedAt=1739878232807)
Let's first take a look at what components I have
.webp?updatedAt=1739878232830)
This is the final effect we want

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
onRefreshapi and we need to pass a callback function into it. -
The data source is specified by
data. -
And
ListHeaderComponentis 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
ascthat controls sorting -
A state
isRefreshingthat controlsloading -
A data source
pdata -
A state
filterthat 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,
fetchItemsis 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.