React Native 3:useEffect 自动请求接口 + 搜索功能 + 封装 Loading 组件

本篇是 React Native 学习系列,我们一次性完成三件事:

  1. 使用 useEffect 实现页面打开自动请求接口,解决无限循环坑

  2. 利用 useEffect 依赖项,实现输入框实时搜索课程

  3. 封装公共 Loading 加载组件,优化网络请求用户体验

前置:Expo 项目,本地后端接口 http://localhost:3000/search,支持 q 查询参数做搜索。

一、useEffect:页面挂载自动请求接口

之前我们写的案例,需要点击按钮才触发请求。真实 App,打开页面就要自动加载数据。

如果你写过 Vue,初始化请求会放在 onMounted;在 React / React Native 中对应的就是 useEffect 这个 Hook。 useEffect 能力很强,可以模拟 Vue 的 onMounted、onUpdated,甚至 watch,最常用场景就是组件挂载后请求接口。

1.1 基础写法(会踩坑版本)

先引入 Hook:

js 复制代码
import { useState, useEffect } from 'react';

把接口请求函数放进 useEffect:

js 复制代码
const [courses, setCourses] = useState([]);

const fetchData = async () => {
  const res = await fetch('http://localhost:3000/search');
  const { data } = await res.json();
  setCourses(data.courses);
};

// ❌ 不写依赖数组,会无限循环
useEffect(() => {
  fetchData();
});

渲染列表,删掉按钮:

jsx 复制代码
return (
  <View style={styles.container}>
    {courses.map((course) => (
      <Text key={course.id}>{course.name}</Text>
    ))}
  </View>
);

运行后打开调试工具(终端按j),你会发现接口疯狂重复请求,无限循环。

问题根源

useEffect 默认规则:组件每一次重新渲染,都会执行 useEffect

  1. 组件初次渲染 → 执行 useEffect → 请求接口

  2. setCourses 修改 state → 组件重渲染

  3. 重渲染后再次执行 useEffect → 再次请求,循环往复

✅ 解决方案:给 useEffect 第二个参数,**空依赖数组 **[]

js 复制代码
// ✅ 空数组:仅组件挂载时执行一次
useEffect(() => {
  fetchData();
}, []);

核心知识点:

  • 不写第二个参数:每次组件渲染都会执行

  • [] 空数组:只在组件首次挂载执行一次

完整基础版 App.js

js 复制代码
import { StyleSheet, Text, View } from 'react-native';
import { useState, useEffect } from 'react';

export default function App() {
  const [courses, setCourses] = useState([]);

  const fetchData = async () => {
    const res = await fetch('http://localhost:3000/search');
    const { data } = await res.json();
    setCourses(data.courses);
  };

  useEffect(() => {
    fetchData();
  }, []);

  return (
    <View style={styles.container}>
      {courses.map((course) => (
        <Text key={course.id}>{course.name}</Text>
      ))}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

二、useEffect 依赖项:实现输入框实时搜索

学会挂载请求,我们进阶:利用依赖项实现搜索。

2.1 接口说明

后端接口支持查询参数 q

  • http://localhost:3000/search?q=node → node 相关课程

  • http://localhost:3000/search?q=react → react 相关课程

2.2 TextInput 输入框组件

React Native 使用 TextInput 实现输入框。

  • onChangeText:监听输入变化,拿到输入文本

  • placeholder:输入框提示文字

js 复制代码
import { TextInput, StyleSheet, Text, View } from 'react-native';
import { useState, useEffect } from 'react';

export default function App() {
  const [courses, setCourses] = useState([]);
  // 搜索关键词state
  const [keyword, setKeyword] = useState('');

  const fetchData = async () => {
    // 把keyword拼接到url参数
    const res = await fetch(`http://localhost:3000/search?q=${keyword}`);
    const { data } = await res.json();
    setCourses(data.courses);
  };

  // 🎯 依赖项设置为keyword
  useEffect(() => {
    fetchData();
  }, [keyword]);

  return (
    <View style={styles.container}>
      <Text>您搜索的关键词是:{keyword}</Text>
      <TextInput
        style={styles.input}
        placeholder="请填写要搜索的课程!"
        onChangeText={(text) => setKeyword(text)}
        defaultValue={keyword}
      />
      {courses.map((course) => (
        <Text key={course.id}>{course.name}</Text>
      ))}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  input: {
    height: 40,
    width: 300,
    margin: 12,
    padding: 10,
    borderWidth: 1,
    borderColor: '#ccc',
    borderRadius: 5,
  },
});
  • placeholder,就和HTML里表单的一样,是用来显示提示信息的。
  • onChangeText,看名字就知道了,和JS里的onchange事件一个意思。
  • 当输入框的数据改变了,可以用text来获取到用户填写的内容。
  • 再调用setKeyword将用户填写的内容,设置给keyword状态。
  • defaultValue就是表单里的默认值。
  • 为了确认到底获取到了没有?可以在最上面,加个显示keyword的代码。

2.3 依赖项原理

useEffect 第二个数组就是依赖项数组:

数组里面的变量一旦发生改变,useEffect 就自动重新执行。

这里我们写 [keyword]: 用户打字 → keyword 更新 → 触发 useEffect → 重新请求接口,刷新列表。 打开调试网络面板可以看到:每输入一个字符就发起一次请求。

知识点汇总

  1. 无第二个参数:每次渲染执行

  2. []:仅挂载执行一次

  3. [xxx]:xxx 变量改变,自动执行副作用

三、封装 Loading 加载组件,优化请求体验

网络请求是异步耗时操作,数据回来之前,页面空白会很影响体验。我们加上 loading 转圈动画。

3.1 增加 loading 状态

新增 loading state,初始值为 true,页面打开默认加载中。 使用 try...finally,无论请求成功 / 失败,finally 都会执行,关闭 loading。

js 复制代码
const [loading, setLoading] = useState(true);

const fetchData = async () => {
  try {
    const res = await fetch(`http://localhost:3000/search?q=${keyword}`);
    const { data } = await res.json();
    setCourses(data.courses);
  } finally {
    setLoading(false);
  }
};
  • 给里面的内容,加上了try...fianlly。
  • 当请求完成后,在finally里面,将加载中状态设置为false。
  • 因为读取的是本地接口,请求的速度会非常非常快,会导致根本看不到什么效果。
  • 我们可以在请求之前后,加上个延迟 2 秒,便于观察效果。

3.2 ActivityIndicator 原生加载指示器

React Native 内置 ActivityIndicator,原生转圈加载动画。 增加判断:如果loading=true,直接返回加载组件,不再渲染搜索和列表。

3.3 抽离公共 Loading 组件

加载组件项目多个页面都会使用,直接抽成独立组件,避免重复代码。

新建文件 components/shared/Loading.js

js 复制代码
import { ActivityIndicator, StyleSheet } from 'react-native';

/**
 * 全局Loading加载组件
 */
export default function Loading() {
  return <ActivityIndicator size="small" color="#1f99b0" style={styles.loading} />;
}

const styles = StyleSheet.create({
  loading: {
    backgroundColor: '#fff',
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    zIndex: 1,
  },
});

3.4 最终完整版 App.js

js 复制代码
import { TextInput, StyleSheet, Text, View } from 'react-native';
import { useState, useEffect } from 'react';
import Loading from './components/shared/Loading';

export default function App() {
  const [courses, setCourses] = useState([]);
  const [keyword, setKeyword] = useState('');
  const [loading, setLoading] = useState(true);

  const fetchData = async () => {
    try {
      const res = await fetch(`http://localhost:3000/search?q=${keyword}`);
      const { data } = await res.json();
      setCourses(data.courses);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    fetchData();
  }, [keyword]);

  // 加载中渲染Loading组件
  if (loading) {
    return <Loading />;
  }

  return (
    <View style={styles.container}>
      <Text>您搜索的关键词是:{keyword}</Text>
      <TextInput
        style={styles.input}
        placeholder="请填写要搜索的课程!"
        onChangeText={(text) => setKeyword(text)}
        defaultValue={keyword}
      />
      {courses.map((course) => (
        <Text key={course.id}>{course.name}</Text>
      ))}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  input: {
    height: 40,
    width: 300,
    margin: 12,
    padding: 10,
    borderWidth: 1,
    borderColor: '#ccc',
    borderRadius: 5,
  },
});

⚠️ 踩坑提醒:Android 模拟器访问 localhost 会请求失败,接口地址要替换成电脑本机 IP。ActivityIndicator 在 iOS 和 Android 原生样式略有差异,属于正常现象。

总结

  1. useEffect 是 RN 函数组件处理副作用的核心,依赖数组是灵魂

    • 空数组 []:挂载执行一次

    • 写入变量:变量变更,自动执行请求

  2. TextInput 实现输入框,onChangeText 监听输入内容,配合 useEffect 依赖项轻松实现实时搜索

  3. 网络请求增加 loading 状态,使用ActivityIndicator;通用 UI 抽离为独立组件,减少重复代码,提升复用性

相关推荐
是你的小橘呀2 小时前
React Native 2:列表渲染 + 后端接口搭建 + Fetch 请求数据
react native
光影少年11 小时前
RN与Flutter架构区别
前端·flutter·react native·react.js·架构·node.js
潇潇潇暮雨1 天前
让 AI 读到 App 实时日志,结合源码定位问题
react native·开源·ai编程
光影少年3 天前
Fabric渲染流程
前端·react native·react.js
墨狂之逸才3 天前
React Native iOS 模拟器启动白屏:一次从日志到主线程采样的排查实录
react native
光影少年3 天前
RN启动流程(bundle加载→Bridge初始化→首屏渲染)
前端·react native·react.js
CHB3 天前
一文讲透原生渲染和自渲染
flutter·react native·uni-app
Flynt11 天前
Shopify 弃 React Native 上了 HN 1272 分,我复现了它给 Agent 用的那套无头架构
react native·前端框架·agent