如何使用React Native构建实时的Todo应用

蒋硕
2023-12-01

A todo app touches on all the important parts of building any data-driven app, including the Create, Read, Update and Delete (CRUD) operations. In this story I’ll be building a todo app with one of the most popular mobile frameworks, React Native.

上构建任何数据驱动的应用程序,包括C reate,R EAD,U PDATE和d elete(CRUD)操作的所有的重要部件的待办事项应用触摸。 在这个故事中,我将使用最受欢迎的移动框架之一 React Native构建Todo应用程序。

I’ll be using ReactiveSearch Native, an open-source library which provides React Native UI components and simplifies building data-driven apps.

我将使用ReactiveSearch Native ,这是一个开放源代码库,它提供React Native UI组件并简化了构建数据驱动型应用程序的过程。

Here’s what I’ll be building in this story:

这是我将在此故事中构建的内容:

Check out the app on snack or on expo.

小吃博览会上查看该应用程序。

什么是React Native? (What is React Native?)

Here’s what the docs say:

这是文档所说的:

React Native lets you build mobile apps using only JavaScript. It uses the same design as React, letting you compose a rich mobile UI from declarative components.
React Native可让您仅使用JavaScript构建移动应用程序。 它使用与React相同的设计,使您可以从声明性组件组成丰富的移动UI。

Even if you’re just getting started with React or React Native, you should be able to follow along with this story and build your very own real-time todo app.

即使您刚刚开始使用React或React Native,您也应该能够跟随这个故事并构建自己的实时todo应用程序。

为什么要使用ReactiveSearch? ⚛ (Why use ReactiveSearch? ⚛)

ReactiveSearch is an open-source React and React Native UI components library for Elasticsearch which I’ve co-authored with some awesome people. It provides a variety of React Native components that can connect to any Elasticsearch cluster.

ReactiveSearch是我和一些很棒的人共同编写的,用于Elasticsearch的开源React和React Native UI组件库。 它提供了各种可以连接到任何Elasticsearch集群的React Native组件。

I’ve written another story on Building a GitHub Repo Explorer with React and Elasticsearch which you may check out for a brief overview of Elasticsearch. Even if you’ve had no experience with Elasticsearch you should be able to follow along with this story fine.

我已经写了另一个关于使用React和Elasticsearch构建GitHub Repo Explorer的故事,您可以查看一下Elasticsearch的简要概述。 即使您没有使用Elasticsearch的经验,您也应该能够很好地理解这个故事。

设置东西⚒ (Setting things up ⚒)

We will be using the React Native version of the library here.

我们将在这里使用库的React Native版本

Before we start building the UI, we’ll need to create a datastore in Elasticsearch. ReactiveSearch works with any Elasticsearch index and you can easily use it with your own dataset.

在开始构建UI之前,我们需要在Elasticsearch中创建一个数据存储。 ReactiveSearch可与任何Elasticsearch索引一起使用,您可以轻松地将其与自己的数据集结合使用

For brevity, you can use my dataset directly or create one for yourself using appbase.io which lets you create a hosted Elasticsearch index (aka app).

为简便起见,您可以直接使用我的数据集 ,也可以使用appbase.io为自己创建一个数据集 ,该数据库可让您创建托管的Elasticsearch索引(又名app)。

All the todos are structured in the following format:

所有待办事项的结构如下:

{
  "title": "react-native",
  "completed": true,
  "createdAt": 1518449005768
}

入门项目 (The starter project)

Before we get started, I would recommend installing yarn. On Linux it can be done simply by adding the yarn repository and running the install command via your package manager. On Mac, you should install Homebrew first to make things simpler. Here are the yarn installation docs for more detail. The next thing which you may install is watchman. Its a file watching service which will help the react-native packager to run smoothly.

在开始之前,我建议您安装yarn 。 在Linux上,只需添加纱线存储库并通过程序包管理器运行install命令即可完成。 在Mac上,您应该先安装Homebrew ,以简化操作。 是纱线安装文档,以获取更多详细信息。 您可能要安装的下一件东西是watchman 。 它的文件监视服务将帮助本机打包程序平稳运行。

I’ve setup the starter project with create-react-native-app in a GitHub branch here. You may download a zip or clone the base branch by running the following command: ?

我在这里的GitHub分支中使用create-react-native-app设置了启动程序项目。 您可以通过运行以下命令下载zip或克隆基本分支:

git clone -b base https://github.com/appbaseio-apps/todos-native
  • Next install the dependencies and start the packager:

    接下来安装依赖项并启动打包程序:
cd todos-native && yarn && yarn start
  • After the packager starts, you may run the app on your phone using the Expo app or using an Android or IOS emulator:

    打包程序启动后,您可以使用Expo应用程序或Android或IOS模拟器在手机上运行该应用程序:

潜入代码? (Diving into code ?)

Once you have cloned the code from the base branch, you should see a directory structure like below:

base分支克隆代码后,您应该会看到如下目录结构:

navigation
├── RootComponent.js         // Root component for our app
├── MainTabNavigator.js      // Tab navigation component
screens
├── TodosScreen.js           // Renders the TodosContainer
components        
├── Header.js                // Header component         
├── AddTodo.js               // Add todo input        
├── AddTodoButton.js         // Add todo floating button
├── TodoItem.js              // The todo item         
├── TodosContainer.js        // Todos main container api
├── todos.js                 // APIs for performing writes
constants                    // All types of constants used in app
types                        // Todo type to be used with prop-types
utils                        // Streaming logic goes here

Let’s breakdown what the base setup comes with:

让我们细分一下基本设置附带的内容:

1.导航 (1. Navigation)

  • All the necessary configurations for connecting to Elasticsearch are at constants/Config.js.

    连接到Elasticsearch的所有必要配置都在constants/Config.js

  • We’re using TabNavigator from react-navigation for showing the All, Active and Completed todos screen. This is rendered by the navigation/RootComponent.js. You’ll notice the RootComponent wraps everything inside the ReactiveBase component from ReactiveSearch. This component provides all the necessary data to the child ReactiveSearch components. You can connect your own Elasticsearch index here by just updating the configurations in constants/Config.js.

    我们从React导航中使用TabNavigator来显示AllActiveCompleted待办事项屏幕。 这由navigation/RootComponent.js 。 您会注意到RootComponent将所有内容包装在ReactiveBase组件内。 该组件将所有必要的数据提供给子级ReactiveSearch组件。 您只需在constants/Config.js更新配置,即可在此处连接您自己的Elasticsearch索引。

The navigation logic is present in navigation/MainNavigator.js. Lets go over how it works. Here are the docs for tab navigation if you wish to reference anything.

导航逻辑存在于navigation/MainNavigator.js 。 让我们回顾一下它是如何工作的。 如果您希望引用任何内容,请参见以下用于标签导航的文档。

import React from 'react';
import { MaterialIcons } from '@expo/vector-icons';
import { TabNavigator, TabBarBottom } from 'react-navigation';

import Colors from '../constants/Colors';
import CONSTANTS from '../constants';
import TodosScreen from '../screens/TodosScreen';

const commonNavigationOptions = ({ navigation }) => ({
    header: null,
    title: navigation.state.routeName,
});

// we just pass these to render different routes
const routeOptions = {
    screen: TodosScreen,
    navigationOptions: commonNavigationOptions,
};

// different routes for all, active and completed todos
const TabNav = TabNavigator(
    {
        [CONSTANTS.ALL]: routeOptions,
        [CONSTANTS.ACTIVE]: routeOptions,
        [CONSTANTS.COMPLETED]: routeOptions,
    },
    {
        navigationOptions: ({ navigation }) => ({
            // this tells us which icon to render on the tabs
            tabBarIcon: ({ focused }) => {
                const { routeName } = navigation.state;
                let iconName;
                switch (routeName) {
                    case CONSTANTS.ALL:
                        iconName = 'format-list-bulleted';
                        break;
                    case CONSTANTS.ACTIVE:
                        iconName = 'filter-center-focus';
                        break;
                    case CONSTANTS.COMPLETED:
                        iconName = 'playlist-add-check';
                }
                return (
                    <MaterialIcons
                        name={iconName}
                        size={28}
                        style={{ marginBottom: -3 }}
                        color={focused ? Colors.tabIconSelected : Colors.tabIconDefault}
                    />
                );
            },
        }),
        // for rendering the tabs at bottom
        tabBarComponent: TabBarBottom,
        tabBarPosition: 'bottom',
        animationEnabled: true,
        swipeEnabled: true,
    },
);

export default TabNav;
  • The TabNavigator function accepts two arguments, the first being the route configurations and the second being the TabNavigator configurations. In the above snippet, we’re passing the configurations for showing a tab navigation bar at the bottom and setting different icons for each tab.

    TabNavigator函数接受两个参数,第一个是路由配置,第二个是TabNavigator配置。 在以上代码段中,我们通过了以下配置:在底部显示标签导航栏,并为每个标签设置不同的图标。

2. TodosScreen和TodosContainer (2. TodosScreen and TodosContainer)

The TodosScreen component in screens/TodosScreen.js wraps our main TodosContainer component in components/TodosContainer.js where we’ll be adding various components for the app. The TodosContainer will show filtered data, based on whether we’re on the All, Active, or Completed tab.

screens/TodosScreen.jsTodosScreen组件将我们的主要TodosContainer组件包装在components/TodosContainer.js ,我们将为该应用程序添加各种组件。 TodosContainer将根据我们是在“ 全部” ,“ 活动”还是“ 已完成”选项卡上显示过滤的数据。

3.用于创建,更新和删除待办事项的API (3. APIs for Creating, Updating and Deleting todos)

The APIs for CUD operations on Elasticsearch are present in api/todos.js . It contains three simple methods add, update and destroy which work with any Elasticsearch index as specified in constants/Config.js. An important point to keep in mind is that each todo item we create will have a unique _id field. We can use this _id field for updating or deleting an existing todo.

api/todos.js中提供了用于Elasticsearch上的CUD操作的API。 它包含addupdatedestroy这三种简单的方法,它们可以与constants/Config.js指定的任何Elasticsearch索引一起使用。 要记住的重要一点是,我们创建的每个待办事项都会有一个唯一的_id字段。 我们可以使用此_id字段来更新或删除现有待办事项。

For our app, we’ll just need three methods for adding, creating or deleting todos. However, you can find a detailed explanation about the API methods at the docs.

对于我们的应用程序,我们只需要添加,创建或删除待办事项的三种方法。 但是,您可以在docs上找到有关API方法的详细说明。

构建组件和UI? (Building the components and UI ?)

Lets start adding some components to complete the functionality of the app.

让我们开始添加一些组件以完成应用程序的功能。

1.添加待办事项 (1. Adding Todos)

We’ll use Fab from native-base to render a floating button for adding todos.

我们将使用来自native-base Fab渲染一个浮动按钮来添加待办事项。

const AddTodoButton = ({ onPress }) => (
  <Fab
      direction="up"
      containerStyle={{}}
      style={{ backgroundColor: COLORS.primary }}
      position="bottomRight"
      onPress={onPress}
  >
      <Icon name="add" />
  </Fab>
);

Now you can use this component in components/TodosContainer.js.

现在,您可以在components/TodosContainer.js使用此组件。

import AddTodoButton from './AddTodoButton';
...
export default class TodosContainer extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        ...
        <AddTodoButton />
      </View>
    );
  }
}

Once we’ve added the button, we’ll see something like this:

添加按钮后,我们将看到以下内容:

Now, when someones clicks on this button, we’ll need to show the input for adding a todo. Lets add the code for this in components/AddTodo.js.

现在,当有人单击此按钮时,我们需要显示用于添加待办事项的输入。 让我们在components/AddTodo.js添加此代码。

class AddTodo extends Component {
  constructor(props) {
    super(props);
    const { title, completed, createdAt } = this.props.todo;
    this.state = {
      title,
      completed,
      createdAt,
    };
  }

  onSubmit = () => {
    if (this.state.title.length > 0) this.props.onAdd(this.state);
    return null;
  };

  setStateUtil = (property, value = undefined) => {
    this.setState({
      [property]: value,
    });
  };

  render() {
    const { title, completed } = this.state;
    const { onBlur } = this.props;
    return (
      <View
        style={{
          flex: 1,
          width: '100%',
          flexDirection: 'row',
          alignItems: 'center',
          paddingRight: 10,
          paddingBottom: 5,
          paddingTop: 5,
        }}
      >
        <CheckBox checked={completed} onPress={() => this.setStateUtil('completed', !completed)} />
        <Body
          style={{
            flex: 1,
            justifyContent: 'flex-start',
            alignItems: 'flex-start',
            paddingLeft: 25,
          }}
        >
          <TextInput
            style={{ width: '90%' }}
            placeholder="What needs to be done?"
            autoFocus
            underLineColorAndroid="transparent"
            underlineColor="transparent"
            blurOnSubmit
            onSubmitEditing={this.onSubmit}
            onChangeText={changedTitle => this.setStateUtil('title', changedTitle)}
            value={title}
            autoCorrect={false}
            autoCapitalize="none"
            onBlur={onBlur}
          />
        </Body>
        <TouchableOpacity
          onPress={() => this.props.onCancelDelete}
          style={{ paddingLeft: 25, paddingRight: 15 }}
        >
          <Ionicons
            name="ios-trash-outline"
            color={`${title.length > 0 ? 'black' : 'grey'}`}
            size={23}
          />
        </TouchableOpacity>
      </View>
    );
  }
}

The main components used here are TextInput, Checkbox and Ionicons with straightforward props. We’re using title and completed from the state. We’ll be passing the props todo, onAdd, onCancelDelete and onBlur from the components/TodosContainer.js. These will help us in adding new todos or resetting the view if you wish to cancel adding todos.

这里使用的主要组件是TextInputCheckboxIonicons以及简单的道具。 我们正在使用title并从state completed 。 我们将通过道具todoonAddonCancelDeleteonBlurcomponents/TodosContainer.js 。 如果您要取消添加待办事项,这些将有助于我们添加新待办事项或重置视图。

Now we can update components/TodosContainer.js with the required changes for rendering AddTodo component:

现在,我们可以使用渲染AddTodo组件所需的更改来更新components/TodosContainer.js

...
import AddTodoButton from './AddTodoButton';
import AddTodo from './AddTodo';
import TodoModel from '../api/todos';
...

// will render todos based on the active screen: all, active or completed
export default class TodosContainer extends React.Component {
  state = {
    addingTodo: false,
  };

  componentDidMount() {
    // includes the methods for creation, updation and deletion
    this.api = new TodoModel('react-todos');
  }

  render() {
    return (
      <View style={styles.container}>
        <Header />
        <StatusBar backgroundColor={COLORS.primary} barStyle="light-content" />
        <ScrollView>
          {this.state.addingTodo ? (
            <View style={styles.row}>
              <AddTodo
                onAdd={(todo) => {
                  this.setState({ addingTodo: false });
                  this.api.add(todo);
                }}
                onCancelDelete={() => this.setState({ addingTodo: false })}
                onBlur={() => this.setState({ addingTodo: false })}
              />
            </View>
          ) : null}
        </ScrollView>
        <AddTodoButton onPress={() => this.setState({ addingTodo: true })} />
      </View>
    );
  }
}

The AddTodo component is rendered inside a ScrollView component. We also pass an onPress prop to the AddTodoButton to toggle the state and conditionally display the AddTodo component based on this.state.addingTodo. The onAdd prop passed to AddTodo also creates a new todo using the add API at api/todos.js.

AddTodo组件在ScrollView组件内部呈现。 我们还将onPress AddTodoButton传递给AddTodoButton以切换状态并基于this.state.addingTodo有条件地显示AddTodo组件。 传递给AddTodoonAdd道具还使用位于api/todos.js AddTodoadd API创建了一个新的待办事项。

After clicking the add button, we’ll see the input for adding a todo like this:

单击添加按钮后,我们将看到用于添加待办事项的输入,如下所示:

2.显示待办事项 (2. Displaying Todos)

After you finish adding a todo, it’s added into Elasticsearch (which we configured in constants/Config.js). All this data can be viewed in realtime by using ReactiveSearch Native components.

完成添加待办事项后,将其添加到Elasticsearch中(我们在constants/Config.js配置)。 使用ReactiveSearch Native组件可以实时查看所有这些数据。

There are over 10 native UI components that the library provides. For our todo app, we will primarily utilize the ReactiveList component to show the state of todos.

该库提供了10多种本机UI组件 。 对于待办事项应用程序,我们将主要利用ReactiveList组件显示待办事项的状态。

Lets add the ReactiveList component and get our todos displaying. We’ll add this component in components/TodosContainer.js and the necessary methods for it to work. Here’s how the ReactiveList will be used:

让我们添加ReactiveList组件并显示待办事项。 我们将在components/TodosContainer.js添加此组件,以及使其工作所需的方法。 这是使用ReactiveList方法:

...
import { ReactiveList } from '@appbaseio/reactivesearch-native';
...

export default class TodosContainer extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Header />
        <StatusBar backgroundColor={COLORS.primary} barStyle="light-content" />
        <ScrollView>
          <ReactiveList
            componentId="ReactiveList"
            defaultQuery={() => ({
              query: {
                match_all: {},
              },
            })}
            stream
            onAllData={this.onAllData}
            dataField="title"
            showResultStats={false}
            pagination={false}
          />
          ...
        </ScrollView>
        <AddTodoButton onPress={() => this.setState({ addingTodo: true })} />
      </View>
    );
  }
}

We haven’t added the onAllData method yet, but let’s understand a bit about the props that we have used here:

我们还没有添加onAllData方法,但是让我们对这里使用的props有所了解:

  • componentId — unique identifier for the component.

    componentIdcomponentId唯一标识符。

  • defaultQuery: the query to be applied initially for the list. We’ll use match_all to show all the todos in default case.

    defaultQuery :最初应用于列表的查询。 在默认情况下,我们将使用match_all显示所有match_all

  • stream: whether to stream new result updates or just show historical results. By setting this to true, we now also listen for the live Todo updates. We’ll add the streaming related logic later.

    stream :是流式传输新结果更新还是仅显示历史结果。 通过将其设置为true ,我们现在还监听实时的Todo更新。 稍后我们将添加与流相关的逻辑。

  • onAllData — a callback function which receives the list of current todo items and the streaming (new todos and any updates) and returns a React component or JSX to render. Here’s how the syntax looks like:

    onAllData —回调函数,用于接收当前待办事项列表和流(新待办事项和任何更新)的流,并返回要渲染的React组件或JSX。 语法如下所示:

<ReactiveList
  onAllData(todos, streamData) {
    // return the list to render
  }
  ...
/>

You can read more about all of these props in detail on the ReactiveList’s docs page.

您可以在ReactiveList的docs页面上详细了解所有这些道具。

To see something, we’ll need to return a JSX or React component from onAllData callback. For this, we will use React Native’s FlatList which is composed of Text components. In the next step we’ll add our custom TodoItem component.

为了看到一些东西,我们需要从onAllData回调中返回一个JSX或React组件。 为此,我们将使用由Text组件组成的React Native的FlatList 。 在下一步中,我们将添加自定义的TodoItem组件。

...
import { ScrollView, StyleSheet, StatusBar, FlatList, Text } from 'react-native';
import CONSTANTS from '../constants';
...

export default class TodosContainer extends React.Component {
  ...
  onAllData = (todos, streamData) => {
    // filter data based on "screen": [All | Active | Completed]
    const filteredData = this.filterTodosData(todos);

    return (
      <FlatList
        style={{ width: '100%', top: 15 }}
        data={filteredData}
        keyExtractor={item => item._id}
        renderItem={({ item: todo }) => (
            <Text>{todo.title}</Text>
        )}
      />
    );
  };

  filterTodosData = (todosData) => {
    const { screen } = this.props;

    switch (screen) {
      case CONSTANTS.ALL:
        return todosData;
      case CONSTANTS.ACTIVE:
        return todosData.filter(todo => !todo.completed);
      case CONSTANTS.COMPLETED:
        return todosData.filter(todo => todo.completed);
    }

    return todosData;
  };

  render() {
    ...
  }
}

3.添加TodoItem (3. Adding TodoItem(s))

Next, we’ll create a separate component TodoItem for showing each todo which will contain all necessary markups for a todo item like the CheckBox, Text, and a delete Icon. This goes in components/TodoItem.js:

接下来,我们将创建一个单独的组件TodoItem来显示每个待办事项,其中将包含待办事项的所有必要标记,例如CheckBoxText和delete Icon 。 这在components/TodoItem.js

class TodoItem extends Component {
  onTodoItemToggle = (todo, propAction) => {
    propAction({
      ...todo,
      completed: !todo.completed,
    });
  };

  render() {
    const { todo, onUpdate, onDelete } = this.props;

    return (
      <View style={styles.row}>
        <View
          style={{
            flex: 1,
            width: '100%',
            flexDirection: 'row',
            alignItems: 'center',
            paddingRight: 10,
            paddingVertical: 5,
          }}
        >
          <TouchableOpacity
            onPress={() => this.onTodoItemToggle(todo, onUpdate)}
            style={{
              flex: 1,
              width: '100%',
              flexDirection: 'row',
            }}
          >
            <CheckBox
              checked={todo.completed}
              onPress={() => this.onTodoItemToggle(todo, onUpdate)}
            />
            <Body
              style={{
                flex: 1,
                justifyContent: 'flex-start',
                alignItems: 'flex-start',
                paddingLeft: 25,
              }}
            >
              <Text
                style={{
                  color: todo.completed ? 'grey' : 'black',
                  textDecorationLine: todo.completed ? 'line-through' : 'none',
                }}
              >
                {todo.title}
              </Text>
            </Body>
          </TouchableOpacity>
          <TouchableOpacity
            onPress={() => onDelete(todo)}
            style={{ paddingLeft: 25, paddingRight: 15 }}
          >
            <Ionicons
              name="ios-trash-outline"
              color={`${todo.title.length > 0 ? 'black' : 'grey'}`}
              size={23}
            />
          </TouchableOpacity>
        </View>
      </View>
    );
  }
}

This component gets the todo from its props along with onDelete and onUpdate which are used to update and delete the todo item respectively. We’re using these at the necessary places using the onPress prop of the components we’re using.

该组件从其道具中获得todo以及分别用于更新和删除待办事项的onDeleteonUpdate 。 我们在必要的地方使用我们正在使用的组件的onPress道具来使用它们。

Next, we can import and use the TodoItem component in our onAllData in components/TodosContainer.js. We’ll pass the todo as a prop along with the API methods for update and destroy which will be used by TodoItem component.

接下来,我们可以在components/TodosContainer.js onAllDataimport并使用TodoItem组件。 我们将把todo作为道具与用于updatedestroy的API方法一起传递,该方法将由TodoItem组件使用。

class TodosContainer extends Component {
  ...
  onAllData = (todos, streamData) => {
    ...
    return (
      <FlatList
        ...
        renderItem={({ item: todo }) => (
          <TodoItem 
            todo={todo}
            onUpdate={this.api.update} 
            onDelete={this.api.destroy}
          />
        )}
      />
    );
  }
}

4.流数据更新 (4. Streaming Data Updates)

You might have noticed that the todos are displaying fine, except you’re unable to view updated todos without refreshing the app. In this final step, we’re going to fit that missing part of the puzzle.

您可能已经注意到待办事项显示正常,除非您不刷新应用程序就无法查看更新的待办事项。 在最后一步,我们将解决难题中缺失的部分。

In the previous section, we added an onAllData method for the ReactiveList component. The second parameter of onAllData receives streaming updates which we’re going to utilize to always keep the todos updated. Here’s how the updated onAllData method will look like in components/TodosContainer.js.

在上一节中,我们为ReactiveList组件添加了onAllData方法。 onAllData的第二个参数接收流更新,我们将利用它来始终保持待办事项的更新。 这是更新后的onAllData方法在components/TodosContainer.js

import Utils from '../utils';
...

export default class TodosContainer extends React.Component {
  ...
  onAllData = (todos, streamData) => {
    // merge streaming todos data along with current todos
    const todosData = Utils.mergeTodos(todos, streamData);

    // filter data based on "screen": [All | Active | Completed]
    const filteredData = this.filterTodosData(todosData);

    return (
      <FlatList
        style={{ width: '100%', top: 15 }}
        data={filteredData}
        keyExtractor={item => item._id}
        renderItem={({ item: todo }) => (
            <TodoItem todo={todo} onUpdate={this.api.update} onDelete={this.api.destroy} />
        )}
      />
    );
  };
  ...
}

The mergeTodos method is present in utils/index.js. Here’s how it works:

mergeTodos方法位于utils/index.js 。 运作方式如下:

class Utils {
  static mergeTodos(todos, streamData) {
    // generate an array of ids of streamData
    const streamDataIds = streamData.map(todo => todo._id);

    return (
      todos
        // consider streamData as the source of truth
        // first take existing todos which are not present in stream data
        .filter(({ _id }) => !streamDataIds.includes(_id))
        // then add todos from stream data
        .concat(streamData)
        // remove todos which are deleted in stream data
        .filter(todo => !todo._deleted)
        // finally sort on the basis of creation timestamp
        .sort((a, b) => a.createdAt - b.createdAt)
    );
  }
}

export default Utils;

The streamData receives an array of todo objects when they’re created, deleted, or updated. If an object is updated, it contains a _updated key set to true. Similarly, if an object is deleted, it contains a _deleted key set to true. If an object is created, it contains neither of the two. Using these points, we’ve added the mergeTodos function.

当创建,删除或更新todo对象时, streamData接收它们的数组。 如果对象被更新,则它包含设置为true_updated键。 同样,如果删除对象,则该对象包含设置为true_deleted键。 如果创建了一个对象,则这两个都不包含。 使用这些要点,我们添加了mergeTodos函数。

With this, you should be able to see the changes to todo items in realtime! If you have an additional device/emulator running the same app, both will stream new updates too. ?

这样,您应该能够实时查看待办事项的更改! 如果您还有其他运行同一应用程序的设备/模拟器,则两者也都将流式传输新更新。 ?

  1. Todos app demo, expo link, starter project and final source code

    Todos应用程序演示博览会链接入门项目最终源代码

  2. ReactiveSearch GitHub repo ⭐️

    ReactiveSearch GitHub存储库 ⭐️

  3. ReactiveSearch docs

    ReactiveSearch 文档

Hope you enjoyed this story. If you have any thoughts or suggestions, please let me know and have fun!

希望您喜欢这个故事。 如果您有任何想法或建议,请让我知道并玩得开心!



You may follow me on twitter for latest updates. I've also started posting more recent posts on my personal blog.

您可以在Twitter上关注我以获取最新更新。 我还开始在我的个人博客上发布更多最新帖子。

Special thanks to Dhruvdutt Jadhav for helping me with this story and the Todos app.

特别感谢 Dhruvdutt Jadhav为我提供了这个故事和Todos应用程序的帮助。

翻译自: https://www.freecodecamp.org/news/how-to-build-a-real-time-todo-app-with-react-native-19a1ce15b0b3/

 类似资料: