样式
在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>
<TextInput
style={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>
<TextInput
style={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,
},
});