在React Native中使用React Query进行状态管理

963 阅读8分钟

简介

状态管理是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文档中提到的一些重要状态。

  • isLoadingstatus === 'loading' - 该查询没有数据,目前正在获取。
  • isErrorstatus === 'error' - 查询遇到了一个错误
  • isSuccessstatus === 'success' - 查询成功,数据可用。
  • isIdlestatus === 'idle' - 该查询目前是禁用的(你将在稍后了解更多这方面的信息)。

一个查询只能处于这些状态中的一种。而在这些主要状态之外,它还包含了更多的信息,比如。

  • error - 如果查询处于isError 状态,则可通过error 属性获得错误信息
  • data - 如果查询处于success 状态,数据可通过data 属性获得。
  • isFetching - 在任何状态下,如果查询在任何时候都在获取(包括后台重新获取),isFetchingtrue

如果没有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 -->

你会被提示有一个模板列表。

Expo CLI Template List

选择第一个,并通过运行改变到你的应用目录。

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>
);

在上面的代码中,我们已经为两个屏幕设置了导航,即HomePost
我们将在一会儿创建PostsPost 组件。

添加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.jsPosts.js 文件--之前,我们需要设置我们的钩子来获取这些屏幕所渲染的数据。

Posts.js 文件渲染所有从JSON Placeholder获取的帖子,它作为主屏幕,而Post.js 文件则处理每个帖子的细节。

创建两个文件夹,即screenshooks 。在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 的唯一标识符作为其第一个参数,将一个获取器函数作为其第二个参数。现在我们可以导入usePostsusePost ,以便在我们的screens 中使用。

screens 文件夹中,创建两个文件,即Posts.jsPost.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 为真,我们就使用

现在我们有了。

RN FlatList for React Query project

接下来,在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 Post Screen

另外,在第一次加载时,你会看到帖子和评论的加载,但在这之后,当你来回浏览时,你不会再看到它们。这是因为React Query在引擎盖下缓存了我们的数据,使后续的加载时间变得 "即时"。

结论

React Query是一个经过深思熟虑的数据获取库,具有很多有趣的功能。正如我们所看到的,它在DXUX ,也提升了我们的应用性能。

我确实希望在阅读完这篇文章后,你已经学到了足够的知识,可以在你的下一个项目中尝试一下。

最后,你可以在GitHub上获得完整的应用程序

The postUsing React Query for state management in React Nativeappeared first onLogRocket Blog.