【React Native】样式、网络请求和Loading
样式
在React Native
里写样式,必须使用StyleSheet.create
来创建。必须要改成驼峰法
来书写。
React Native
里的排版布局,默认是使用flex
的。也就说页面中的元素,默认就自带flex
属性了,不需要自己额外添加display: 'flex'
。
与Web
不同的是,React Native
里,flexDirection
,默认是column
,而不是row
。这样子,页面上的元素,默认会从上向下排列。除此外,还有些其他属性的默认值不同,但用法都是和CSS
一样的。
fontSize
,后面的值,没有单位
。在React Native
里,这种值,是与设备像素密度无关的逻辑像素点,千万不要加上px
。
样式可以写成行内样式,也可以写成对象的形式,还可以同时使用:
<Text style={[styles.title, { fontSize: 50, width: 200 }]}>欢迎!
</Text>
- 优先规则,并不是行内样式优先。而是后面的,会把前面的覆盖。
简单的一个demo,点击按钮count+1:
import { Button, StyleSheet, Text, View } from 'react-native';
import { useState } from 'react';export default function App() {const [count, setCount] = useState(0);return (<View style={styles.container}><Text>{count}</Text><Button title="Click me" onPress={() => setCount(count + 1)} /></View>);
}
网络请求
一定要确保手机和电脑在同一局域网内,也就是说连的是同一台路由器。
在React Native
里读取接口,不需要额外安装任何依赖包,它里面自带Fetch API。
/*** 获取搜索接口课程数据* @returns {Promise<void>}*/
const fetchData = async () => {const res = await fetch('http://localhost:3000/search');const { data } = await res.json();setCourses(data.courses);
};
- 获取到的数据是
JSON
格式的,这里要用.json()
来解析一下。
使用
Android
模拟器,必须改为局域网 IP
地址,用localhost
无法读取接口。关于局域网 IP
地址的获取方法,请查看获取 IP 地址命令 。
使用 React Native 调试工具, 继续在终端里,按m
键,App
上会弹出菜单,使用Open JS Debugger
,就会打开调试工具
。直接在终端里按j
键,也可以打开调试工具
。
如果调试工具里,没有显示正确的数据。可能是调试工具,连接到之前运行的 App 上去了。可以在模拟器里或者真机里,将运行的 App 退出,然后按 i、a 或者扫码重新运行。
这里有个需要注意的问题,如果用手机真机来预览,在扫码后,点击按钮,会发现没有任何反应。
这是因为代码里写的接口地址是localhost
,这是本机
的意思,而手机上哪里有接口呢?所以根本请求不到。
咱们的接口是运行在电脑上的,解决方法就是要将请求地址,改成电脑的局域网 IP
地址。
macOS
与Windows
查看IP
的命令不同。
- macOS:
ifconfig | grep "inet " | grep -v 127.0.0.1
- Windows:
ipconfig | findstr "IPv4"
我这里查出来有两个IP
地址。这是因为电脑既插了网线,也连了Wi-Fi
。它们两个都是正确的局域网 IP
地址,任选其一就行。前面的http
和后面的3000
端口是不能丢的,只改localhost
这里。
Windows
电脑有可能还是访问失败,如果碰到这种情况了,关闭Windows
系统设置里的防火墙
。然后用手机浏览器直接访问:http://你的局域网IP地址:3000
,确认接口是有响应的。- 切换网络,重启电脑,都可能会导致
局域网 IP
变更。这时候需重新获取IP
地址,重新配置。
搜索功能 - 传参:
import { Button, StyleSheet, Text, TextInput, View } from "react-native";
import { useEffect, useState } from "react";export default function App() {const [courses, setCourses] = useState([]);const [keyword, setKeyword] = useState("");/*** 获取搜索接口课程数据* @returns { Promise<void> }*/const fetchData = async () => {const res = await fetch(`http://192.168.xx.xx:3000/search?q=${keyword}`);const { data } = await res.json();setCourses(data.courses);console.log("获取到的数据是:", data.courses);};useEffect(() => {fetchData();}, [keyword]);return (<View style={styles.container}><Text>请输入关键字</Text><TextInputstyle={styles.input}placeholder="请输入关键字"value={keyword}onChangeText={setKeyword}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,},
});
Loading
import { useEffect, useState } from "react";
import { StyleSheet, Text, TextInput, View } from "react-native";
import Loading from "./components/shared/Loading";export default function App() {const [courses, setCourses] = useState([]);const [keyword, setKeyword] = useState("");const [loading, setLoading] = useState(true);/*** 获取搜索接口课程数据* @returns { Promise<void> }*/const fetchData = async () => {try {setLoading(true);await new Promise((resolve) => setTimeout(resolve, 2000));const res = await fetch(`http://192.168.xx.xx:3000/search?q=${keyword}`);const { data } = await res.json();setCourses(data.courses);console.log("获取到的数据是:", data.courses);} catch (error) {console.log("获取数据失败:", error);} finally {setLoading(false);}};useEffect(() => {fetchData();}, [keyword]);return (<View style={styles.container}><Text>请输入关键字</Text><TextInputstyle={styles.input}placeholder="请输入关键字"value={keyword}onChangeText={setKeyword}defaultValue={keyword}/>{loading ? (<Loading />) : (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,},
});
import { ActivityIndicator, StyleSheet } from "react-native";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,},
});