写点什么

React-native 实战系列

作者:溪抱鱼
  • 2025-06-04
    河南
  • 本文字数:6216 字

    阅读完需:约 20 分钟

文章总结:

这次我学习了 React Native 实战中表单组件的封装懒加载列表的实现!🛠️ 在表单方面,文章教我封装了常用的 Input, Select, TogglingDateTimePicker 等组件,让它们可复用还能自定义样式,💡 但要注意 Android DateTimePicker 可能需要特别适配。


懒加载列表部分,文章介绍了如何利用 FlatList 实现按需加载数据,并且处理了搜索和排序逻辑,👍 感觉对性能和用户体验提升很有帮助!文章还提到可以从分页、滚动监听和图片懒加载等方面进一步优化

总之,学到了组件封装和列表优化的实用技巧!如果有问题,作者欢迎大家通过 [联系我] 交流哦!💬

1、组件的使用

移动端也有表单,表单也是我经常接触的一个场景,那么让我们就实现一些表单组件吧。

1.1、封装一个 input

我们先从rnTextInput输入框开始


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>  )}
复制代码


让我们先看看在屏幕上这些输入框是这样的。

secureTextEntry

密码输入盘,你的输入会被***挡住。

Return key,这个值决定你的小键盘右下角的值。



我们也可以通过keyboardType去控制弹出的小键盘类型,比如numeric



1.2、封装一个select

首先


expo install @react-native-picker/picker
复制代码


然后我们可以用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>  )}
复制代码

样式


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> );}
复制代码

最终效果如下:

1.3、封装一个 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> )}
复制代码

效果如下:


1.3、实现一个 DateTimePicker


首先下载依赖



expo install @react-native-community/datetimepicker
复制代码


然后我们开发一个iosDateTimePicker组件


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>  )}
复制代码

ios上的表现

安卓会稍微麻烦一些


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> )}
复制代码

安卓上的表现


2、如何实现一个懒加载列表

假设现在有一个需求需要我们实现一个懒加载的列表,它还可以过滤排序下拉刷新。首先我们需要一个手动定义一个数据源,我们可以通过迭代器,实现一个无线调用递增的promise去模拟api的调用,当每次调用fetchItems的时候,我们的cnt都会增加 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), }), })}
复制代码

当我们定义了数据源后我们画一张整体的组件图吧。


我们先看一下我有哪些组件



这是我们最后想要的一个效果


ok,理论来说我们在写组件是应该跟随数据流,自上而下的写,但因为自下而上比较好让大家理解一点,我们就从里面往外面写。

我们先从FlatList开始,大家应该对它很陌生,它是一个rn的内置组件,那么要实现我们的功能需要哪些api那。

  • 首先是懒加载我们需要使用onEndReached如果我们不指定距离,它会在滑动到内容最底部的距离为当前列表可见长度的一半时触发,接受一个函数。

  • 要实现下拉刷新,我们用onRefresh这个api我们需要传一个回调函数进去。

  • 而数据源是由data指定。

  • ListHeaderComponent就是一个表头。

当我们已经知道需要哪些api了,就可以尝试写一下一个最基础的list组件

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,}
复制代码


紧接着我们就需要去实现ListControlsListContainer

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 这里autoFocus是进来就自动聚焦调出小键盘。


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,}
复制代码

可以看到在这三个部分并没有太多的逻辑代码,都是一些用于展示的代码。我把所有的逻辑代码都放在了container中。container,此时想想我们需要在外层容器提供哪些状态。

  • 一个控制排序的状态asc

  • 一个控制loading的状态isRefreshing

  • 一个数据源pdata

  • 一个控制搜索过滤框的状态filter然后我们需要处理哪些逻辑* 搜索排序后的列表展示 fetchItem -> data -> filterAndSort(data)-> pdata=useMemo(data)* 每次往下拉的数据获取 fetchItems* 刷新后拉取新数据 refreshItems那么到这里我们就完成了一个懒加载搜索排序下拉刷新的列表

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]) }) }
//我这的刷新模拟的是拉取新数据的过程 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
复制代码


用户头像

溪抱鱼

关注

还未添加个人签名 2025-02-09 加入

还未添加个人简介

评论

发布
暂无评论
React-native实战系列_前端_溪抱鱼_InfoQ写作社区