简介
状态管理是React Native开发的一个重要组成部分。
状态是一个JavaScript对象,用于处理组件中可变的React Native数据。
在React Native组件中,状态可能会随着时间的推移而发生变化,这主要是由于用户事件造成的。
React Native中一个简单的本地状态可以用setState 方法或useState 钩子来管理。
然而,随着我们的应用越来越大,你可能需要一个状态管理库,因为管理跨组件共享的状态变得越来越困难。
React Native的数据获取库
所以,状态管理是你在开发React应用时可能遇到的常见问题之一。而且这可能是一个很难掌握的方面,因为React中有大量的状态管理库。
下面是一些流行的库。
上下文
数据在React中是单边流动的,从上到下--父组件到子组件。Context API提供了一种通过组件树传递数据的方法,而不需要钻研道具。
Redux
Redux是一个与框架无关的状态管理层次。它非常强大,非常适合大型应用。你可以将Redux用于React、React Native、Vue等。
Redux提供了一个单一的全局状态对象,称为存储,它使用了几个概念,如行动、行动创建者和还原器。在大多数情况下,你会将Redux与Redux Thunk或Redux-Saga等中间件一起使用,这引入了更多的复杂性,因为你有更多的东西需要学习。然而,Redux对于客户端的状态管理是非常好的。
你可以在这里得到一份使用Redux和React Native的全面指南。
React查询
本文主要讨论React Native中使用React Query的状态管理,这项技术自称是React缺少的数据获取库。
让我们在下一节中了解更多关于React Query的信息。
什么是React Query 3?
React Query是一个轻量级的缓存层,它存在于我们的应用程序中。作为一个数据获取库,它与我们获取数据的方式无关。React Query唯一需要知道的是由Axios或Fetch返回的承诺。
React Query的两个主要概念是查询和变异。查询处理的是获取数据,而突变处理的是在服务器上修改数据。
React Query输出一个useQuery 钩子来处理查询。useQuery 钩子需要两个参数。第一个参数是一个唯一的标识符,描述我们正在获取的东西。第二个标识符是获取器函数--一个异步函数,负责获取你的数据或抛出一个错误。
请看下面的代码。
import { useQuery } from 'react-query';
import axios from 'axios';
const fetchPosts = async () => {
const { data } = await axios.get('https://jsonplaceholder.typicode.com/posts');
return data;
};
const usePosts = () => useQuery('posts', fetchPosts);
useQuery 钩子返回一个查询对象,同时处理所有围绕我们获取的生命周期,所以我们不需要使用useEffect 。
查询对象包括React Query文档中提到的一些重要状态。
isLoading或status === 'loading'- 该查询没有数据,目前正在获取。isError或status === 'error'- 查询遇到了一个错误isSuccess或status === 'success'- 查询成功,数据可用。isIdle或status === 'idle'- 该查询目前是禁用的(你将在稍后了解更多这方面的信息)。
一个查询只能处于这些状态中的一种。而在这些主要状态之外,它还包含了更多的信息,比如。
error- 如果查询处于isError状态,则可通过error属性获得错误信息data- 如果查询处于success状态,数据可通过data属性获得。isFetching- 在任何状态下,如果查询在任何时候都在获取(包括后台重新获取),isFetching。true
如果没有React Query,这些状态中的每一个可能都会使用不同的useState 钩子来处理。所以我们可以看到,React Query帮助我们减少了我们的模板。这是对开发者经验的一种增益--DX 。
另外,React Query还有助于开箱即用的缓存。这对我们的终端用户有直接的影响,因为它使我们的应用程序感觉更快,反应更灵敏。
在这篇文章中,我们将研究如何使用React Query来管理React Native状态。让我们在下一节开始讨论先决条件。
前提条件
- JavaScript的基本知识
- React和React Native的基本知识
开始使用React Query和React Native
首先,我们使用Expo CLI创建一个新的React Native应用程序,如下图所示。
npm i --global expo-cli
// bootstrap a new project
expo init <-- your app name -->
你会被提示有一个模板列表。

选择第一个,并通过运行改变到你的应用目录。
cd <-- your app name -->
接下来,安装依赖项。
// install dependencies
npm i @react-navigation/native @react-navigation/stack axios
react-native-gesture-handler react-native-safe-area-context react-native-screens
react-query
启动开发服务器。
npm start
在下一节中,我们将通过使用JSON Placeholder的数据构建一个简单的应用程序来深入了解React Query。
用React Query进行复杂的状态管理
设置样式
我们将首先创建一个constants 文件夹,里面有一个color.js 文件。在color.js 文件中添加以下代码。
export default {
primary: '#202c41',
border: '#c6c6c6',
white: '#fff',
gray: '#9ca5ab'
};
这是用来给我们的应用程序设置样式的。
设置导航
接下来,我们设置我们的导航。创建一个导航文件夹,里面有一个Main.js 文件。
在Main.js 文件中添加以下代码。
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import { Posts } from '../screens/Posts';
import { Post } from '../screens/Post';
const MainStack = createStackNavigator();
export const Main = () => (
<MainStack.Navigator>
<MainStack.Screen name="Home" component={Posts} />
<MainStack.Screen name="Post" component={Post} />
</MainStack.Navigator>
);
在上面的代码中,我们已经为两个屏幕设置了导航,即Home 和Post 。
我们将在一会儿创建Posts 和Post 组件。
添加React查询
现在我们必须设置React Query。修改我们的App.js 文件,如下图所示。
import React from 'react';
import { StatusBar } from 'expo-status-bar';
import { NavigationContainer } from '@react-navigation/native';
import { QueryClient, QueryClientProvider } from 'react-query';
import { Main } from './navigation/Main';
const queryClient = new QueryClient();
export default function App() {
return (
<React.Fragment>
<StatusBar style="auto" />
<NavigationContainer>
<QueryClientProvider client={queryClient}>
<Main />
</QueryClientProvider>
</NavigationContainer>
</React.Fragment>
);
}
在上面的代码中,React Query使用QueryClient 来与一个缓存进行交互。而QueryClientProvider 组件被用来连接并提供给我们的应用程序QueryClient 。
创建应用程序的屏幕
在这之后,让我们为我们的屏幕创建可重复使用的组件。创建一个components 文件夹,里面有一个Text.js 文件。将以下代码添加到Text.js 。
import React from 'react';
import { StyleSheet, Text as RNText } from 'react-native';
import colors from '../constants/colors';
const styles = StyleSheet.create({
defaultText: {
color: colors.primary,
fontSize: 18
}
});
export const Text = ({ children, style = {} }) => {
const textStyles = [ styles.defaultText ];
textStyles.push(style);
return <RNText style={textStyles}>{children}</RNText>;
};
上面,我们有一个具有默认风格的可重复使用的Text 组件。我们将在整个应用程序中使用它来为我们的文本设置样式。
在我们创建屏幕--Post.js 和Posts.js 文件--之前,我们需要设置我们的钩子来获取这些屏幕所渲染的数据。
Posts.js 文件渲染所有从JSON Placeholder获取的帖子,它作为主屏幕,而Post.js 文件则处理每个帖子的细节。
创建两个文件夹,即screens 和hooks 。在hooks 文件夹中,创建一个usePosts.js 文件并添加以下代码。
import { useQuery } from 'react-query';
import axios from 'axios';
const fetchPosts = async () => {
const { data } = await axios.get('https://jsonplaceholder.typicode.com/posts');
return data;
};
const usePosts = () => useQuery('posts', fetchPosts);
export default usePosts;
同时,创建一个usePost.js 文件和以下代码。
import { useQuery } from 'react-query';
import axios from 'axios';
const fetchPost = async (postId) => {
const { data } = await axios.get(`https://jsonplaceholder.typicode.com/comments?postId=${postId}`);
return data;
};
const usePost = (postId) => useQuery([ 'posts', postId ], () => fetchPost(postId));
export default usePost;
在上面的代码例子中,useQuery 将一个名为posts 的唯一标识符作为其第一个参数,将一个获取器函数作为其第二个参数。现在我们可以导入usePosts 和usePost ,以便在我们的screens 中使用。
在screens 文件夹中,创建两个文件,即Posts.js 和Post.js 。将以下代码添加到Posts.js 文件中。
import React from 'react';
import { View, StyleSheet, FlatList, TouchableOpacity } from 'react-native';
import usePosts from '../hooks/usePosts';
import { Text } from '../components/Text';
import colors from '../constants/colors';
export const Posts = ({ navigation }) => {
const { data, isLoading, isSuccess } = usePosts();
return (
<View style={styles.container}>
{isLoading && (
<React.Fragment>
<Text>Loading...</Text>
</React.Fragment>
)}
{isSuccess && (
<React.Fragment>
<Text style={styles.header}>all posts</Text>
<FlatList
data={data}
style={styles.wrapper}
keyExtractor={(item) => `${item.id}`}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() =>
navigation.push('Post', { post: item })}
style={styles.post}
>
<View style={styles.item}>
<Text style={styles.postTitle}>
{item.title}
</Text>
</View>
</TouchableOpacity>
)}
/>
</React.Fragment>
)}
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.white,
padding: 10
},
wrapper: {
flex: 1,
paddingVertical: 30
},
item: {
paddingVertical: 10,
paddingHorizontal: 20
},
header: {
textAlign: 'center',
textTransform: 'capitalize',
fontWeight: 'bold',
fontSize: 30,
color: colors.primary,
paddingVertical: 10
},
post: {
backgroundColor: colors.primary,
padding: 15,
borderRadius: 10,
marginBottom: 20
},
postTitle: { color: colors.white, textTransform: 'capitalize' }
});
在上面的代码中,如果isLoading ,我们会渲染一个loading… 的文本。而我们在渲染我们的帖子列表时使用了 [FlatList](https://blog.logrocket.com/deep-dive-react-native-flatlist/)如果isSuccess 为真,我们就使用
现在我们有了。

接下来,在Post.js 文件中,添加以下代码。
import React from 'react';
import { View, StyleSheet, ScrollView } from 'react-native';
import usePost from '../hooks/usePost';
import { Text } from '../components/Text';
import colors from '../constants/colors';
export const Post = ({ route }) => {
const { post } = route && route.params;
const { data: comments, isSuccess, isLoading } = usePost(post.id);
return (
<ScrollView style={styles.container}>
<Text style={styles.header}>{post.title}</Text>
<View style={styles.post}>
<Text>{post.body}</Text>
</View>
{isLoading && <Text style={{ textAlign: 'center' }}>Loading...</Text>}
{isSuccess && (
<React.Fragment>
<Text style={styles.commentHeader}>Comments</Text>
{comments.map((comment) => (
<View key={comment.id} style={styles.post}>
<Text>{comment.body}</Text>
<Text>{comment.email}</Text>
</View>
))}
</React.Fragment>
)}
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.white,
padding: 30
},
header: {
textAlign: 'center',
textTransform: 'capitalize',
fontWeight: 'bold',
fontSize: 40,
color: colors.primary,
paddingVertical: 10
},
commentHeader: {
textAlign: 'center',
textTransform: 'capitalize',
fontWeight: 'bold',
fontSize: 30,
color: colors.primary,
paddingVertical: 30
},
post: {
flex: 1,
paddingVertical: 10,
alignItems: 'center'
}
});
在上面的代码中,我们接收了用户点击的帖子作为路由参数,并使用其ID ,获取其评论。下面是一个帖子屏幕的例子。

另外,在第一次加载时,你会看到帖子和评论的加载,但在这之后,当你来回浏览时,你不会再看到它们。这是因为React Query在引擎盖下缓存了我们的数据,使后续的加载时间变得 "即时"。
结论
React Query是一个经过深思熟虑的数据获取库,具有很多有趣的功能。正如我们所看到的,它在DX 和UX ,也提升了我们的应用性能。
我确实希望在阅读完这篇文章后,你已经学到了足够的知识,可以在你的下一个项目中尝试一下。
最后,你可以在GitHub上获得完整的应用程序。
The postUsing React Query for state management in React Nativeappeared first onLogRocket Blog.