react中使用构建缓存_如何使用React和GraphQL构建实时聊天应用

react中使用构建缓存

GraphQL and React are a match made in heaven. Their ecosystem provides a lot of tools to simplify common web programming tasks including realtime integrations. In this article, you're going to learn how to use only React and GraphQL to make a realtime apps (precisely a chat app).

GraphQL和React是天作之合。 他们的生态系统提供了许多工具来简化常见的Web编程任务,包括实时集成。 在本文中,您将学习如何仅使用React和GraphQL制作实时应用程序(准确地说是聊天应用程序)。

您将学到什么 ( What You Will Learn )

  • How to setup a React + GraphQL project

    如何设置一个React + GraphQL项目
  • Using Apollo to interact with a GraphQL server from React

    使用Apollo与React的GraphQL服务器进行交互
  • Querying and mutating entities

    查询和变异实体
  • Realtime messaging and subscription

    实时消息传递和订阅

I'm assuming you know the basic concepts of GraphQL and React. Therefore, this article will focus on showing you how to build a realtime app with both tools while skipping explanations for basic concepts.

我假设您知道GraphQL和React的基本概念。 因此,本文将重点向您展示如何使用这两种工具构建实时应用程序,同时跳过基本概念的说明。

GraphQL,Apollo,Graphcool,Duh ... ( GraphQL, Apollo, Graphcool, Duh... )

Yes I understand these terms can be daunting. They were not so easy for me to get along with as well but with practice and knowing the fundamental knowledge, you will start getting a hang of it.

是的,我知道这些术语可能令人生畏。 它们对我来说也不是那么容易相处,但是通过实践并了解基本知识,您将开始了解它。

GraphQL (GraphQL)

Basically, GraphQL is a query spec that is built around the Graph algorithm. It's developed by Facebook and it's so powerful that it seems to be replacing the REST we love. Rather than just sending JSON payloads through REST to a server, then the server in-turn queries a database, GraphQL gives you the power to serve these queries right from the client. This way you end up with just what the client needs and not what the REST endpoint exposed.

基本上,GraphQL是围绕Graph算法构建的查询规范。 它是由Facebook开发的,功能如此强大,似乎正在取代我们喜欢的REST。 GraphQL不仅可以通过REST将JSON负载发送到服务器,还可以依次查询数据库,而您可以直接从客户端为这些查询提供服务。 这样,您最终只能得到客户端需要的内容,而不会得到REST端点公开的内容。

阿波罗 (Apollo)

GraphQL is just a spec that you can follow to build your own implementation. But that's a hard journey. This is why tools like Apollo were built to serve as GraphQL clients. From a beginner perspective, you can see it as an SDK for interacting with a GraphQL server that follows a GraphQL spec.

GraphQL只是您可以遵循以建立自己的实现的规范。 但这是艰难的旅程。 这就是为什么像Apollo这样的工具被构建为可以用作GraphQL客户端的原因。 从初学者的角度来看,您可以将其视为用于与遵循GraphQL规范的GraphQL服务器进行交互的SDK。

图酷 (Graphcool)

Speaking of servers, the process of setting up one and the tooling involved can also get overwhelming. Sometimes you might even get it wrong, hence exposing your products to security vulnerabilities. Graphcool is a highly extensive hosted GraphQL server that you can work with. You can manipulate anything on the server using its serverless functions. The service has a free tier that is generous enough to get you into business without paying a dime.

说到服务器,设置服务器和所涉及的工具的过程也可能不堪重负。 有时您甚至可能会弄错它,从而使您的产品暴露于安全漏洞中。 Graphcool是可以使用的高度扩展的托管GraphQL服务器。 您可以使用服务器的无服务器功能来操纵服务器上的任何东西。 该服务提供免费的免费套餐,足以让您无需花一分钱就可以开展业务。

设置一个React项目 ( Setting Up a React Project )

create-react-app has been developers favorite tool for scaffolding React apps. We're just going to stick to it because there is a high chance you know how it works and probably used it before. Scaffold a new project in React by running the following command:

create-react-app已成为开发人员最喜欢用来搭建React应用程序的工具。 我们将坚持下去,因为您很有可能知道它是如何工作的,并且很有可能在以前使用过它。 通过运行以下命令在React中搭建新项目:

create-react-app graphql-chat

This would create a new folder named graphql-chat and download all the necessary React files you need to work with.

这将创建一个名为graphql-chat的新文件夹,并下载您需要使用的所有必需的React文件。

Update the dependencies in the package.json as follows:

如下更新package.json的依赖项:

"dependencies": {

  "apollo-client-preset": "^1.0.6",
  "apollo-link-ws": "^1.0.4",
  "graphql": "^0.12.3",
  "graphql-tag": "^2.6.1",
  "react": "^16.2.0",
  "react-apollo": "^2.0.4",
  "react-dom": "^16.2.0",
  "react-scripts": "1.0.17",
  "subscriptions-transport-ws": "^0.9.4"
},

Then run the install command to download all of these files:

然后运行install命令下载所有这些文件:

npm install
# OR
yarn

We will learn about what each of these dependencies do when we encounter them.

当我们遇到这些依赖关系时,我们将了解它们的作用。

Some global CSS files needs to be included so as to relieve styling concerns from us for this project. Update the head tag in public/index.html to include the following CSS files:

需要包含一些全局CSS文件,以减轻我们对该项目的样式问题。 更新public/index.htmlhead标记以包括以下CSS文件:

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css" />

设置GraphQL实例 ( Setting up a GraphQL Instance )

Rather than have a local server running, we can use an existing free hosted service to setup a GraphQL instance. This is not only free but less tasking and easier to get started with. Graphcool is fantastic for small projects and also amazing for larger projects when your app grows.

无需运行本地服务器,我们可以使用现有的免费托管服务来设置GraphQL实例。 这不仅是免费的,而且任务量更少,并且更容易上手。 当您的应用程序扩展时,Graphcool对于小型项目而言非常棒,对于大型项目而言也很棒。

To setup an instance, you need to first install the Graphcool CLI tool. This tool exposes commands for creating new GraphQL servers, updating them, as well as deploying them. It can also be used to manage the Graphcool cloud functions. Run the following command to install:

要设置实例,您需要首先安装Graphcool CLI工具。 该工具提供了用于创建新GraphQL服务器,更新它们以及部署它们的命令。 它也可以用于管理Graphcool云功能。 运行以下命令进行安装:

npm install -g graphcool-framework

Navigate to the React app we just setup through the terminal and start a new Graphool server:

导航到我们刚刚通过终端设置的React应用,并启动新的Graphool服务器:

graphcool-framework init server

This command will create a folder in the React project named server. The server command contains type and schema definitions for your GraphQL data. You can refer to Understanding GraphQL API Queries as well as the articles in this search result to learn more about GraphQL fundamentals.

此命令将在React项目中创建一个名为server的文件夹。 server命令包含GraphQL数据的类型和架构定义。 您可以参考了解GraphQL API查询以及此搜索结果中的文章,以了解有关GraphQL基础的更多信息。

For our chat app, we just need to focus on the types.graphql file generated in the server folder. This is where you tell GraphQl what the structure of your data looks like. It's known as a type definition. Replace it's content with the following:

对于我们的聊天应用程序,我们只需要关注服务器文件夹中生成的types.graphql文件。 在这里,您可以告诉GraphQl数据的结构。 它被称为类型定义。 将其内容替换为以下内容:

type Chat@model {
  id: ID! @isUnique
  from: String!
  content: String!
  createdAt: DateTime!
}

You need to deploy this schema to the Graphcool server:

您需要将此架构部署到Graphcool服务器:

graphcool-framework deploy

This will first open a browser for you to setup a Graphcool account and then deploy your instance. You can open the instance from the menu on the top left of your Graphcool dashboard:

这将首先打开一个浏览器,供您设置Graphcool帐户,然后部署您的实例。 您可以从Graphcool仪表板左上方的菜单中打开实例:

Back in the terminal, the process prints some important URL you need to interact with your Graphcool server. Please store this URL where you can refer to them:

回到终端,该过程将打印一些与Graphcool服务器进行交互所需的重要URL。 请将该URL存储在可以引用它们的位置:

Once you have the app deployed, open the playground to test if the deploy process has all your type definition in place:

部署完应用程序后,打开操场测试部署过程中是否所有类型定义都已到位:

graphcool-framework playground

Run the following query and mutation in the editor on the left and click the execute button to execute them:

在左侧的编辑器中运行以下查询和变异,然后单击执行按钮以执行它们:

queryFETCH_CHATS{
      allChats{
        from,
        content
      }
    }

    mutation CREATE_CHAT {
      createChat(content: "test", from: "test") {
        content,
        from
      }
    }

The playground would ask you which of the commands you want to run:

游乐场会询问您要运行哪个命令:

提供GraphQL实例进行React ( Provide GraphQL Instance to React )

A React project is ready, and a GraphQL server is cooked. What next? We need to tie them together with all those modules we installed with the package.json file.

一个React项目已经准备好,并且GraphQL服务器已经准备好。 接下来是什么? 我们需要将它们与通过package.json文件安装的所有模块结合在一起。

Let's start with importing them. Update the src/index.js entry file to import the following dependencies:

让我们从导入它们开始。 更新src/index.js条目文件以导入以下依赖项:

import { ApolloProvider } from 'react-apollo';
    import { ApolloClient } from 'apollo-client';
    import { HttpLink } from 'apollo-link-http';
    import { InMemoryCache } from 'apollo-cache-inmemory';
    import { ApolloLink, split } from 'apollo-client-preset'
    import { WebSocketLink } from 'apollo-link-ws'
    import { getMainDefinition } from 'apollo-utilities'

Right under the imports, configure the WebSocket link. You can do this using the WebSocketLink module:

在导入下,配置WebSocket链接。 您可以使用WebSocketLink模块执行此操作:

const wsLink = new WebSocketLink({

  uri: '[Subscriptions API URI]',
  options: {
    reconnect: true
  }
})

The constructor function takes an object with config options. The uri is required and should be the same as the Subscriptions API URI you received after deploying. The options are optional, but we are using the reconnect option to ask WebSocket to try reconnecting after a failed attempt.

构造函数使用带有config选项的对象。 uri是必填项,并且应与部署后收到的Subscriptions API URI相同。 这些选项是可选的,但我们使用reconnect选项要求WebSocket在尝试失败后尝试重新连接。

We are not just making a WebSocket connection. We also need to setup an HTTP connection for request-response operations. Add this right below the WebSocket link setup:

我们不仅在建立WebSocket连接。 我们还需要为请求-响应操作设置HTTP连接。 在WebSocket链接设置下面添加以下代码:

const httpLink = new HttpLink({ uri: '[SIMPLE API URI]' })

The same as the WebSocketLink constructor but uses the Simple API URI. We don't need to pass in any configuration option.

WebSocketLink构造函数相同,但使用简单API URI。 我们不需要传递任何配置选项。

At this point, we have two links. How do we tell the server when to use which. This is achievable using the split method we imported above:

至此,我们有两个链接。 我们如何告诉服务器何时使用哪个。 这可以使用我们上面导入的split方法来实现:

const link = split(

  ({ query }) => {
    const { kind, operation } = getMainDefinition(query)
    return kind === 'OperationDefinition' && operation === 'subscription'
  },
  wsLink,
  httpLink,
)

The split method takes three arguments. The first is a test that returns a boolean. If the boolean value is true, the request is forwarded to the second (wsLink) argument. If false, it's forwarded to the third (httpLink) argument.

split方法采用三个参数。 第一个是返回布尔值的测试。 如果布尔值是true ,那么请求将转发到第二个( wsLink )参数。 如果为false,则将其转发到第三个( httpLink )参数。

Now we can create an Apollo client with the returned link:

现在,我们可以使用返回的链接创建一个Apollo客户端:

const client = new ApolloClient({

  link,
  cache: new InMemoryCache()
})

You can make requests directly to your server using the URLs provided. This is a bit messier than using a wrapper library that provides functionalities to simplify server interaction. Apollo is one of such libraries.

您可以使用提供的URL直接向服务器发出请求。 这比使用提供功能以简化服务器交互的包装器库更为混乱。 阿波罗就是这样的图书馆之一。

Provide the client using the AppProvider component:

使用AppProvider组件提供客户端:

ReactDOM.render(

  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>,
  document.getElementById('root')
);

查询GraphQL服务器 ( Querying the GraphQL Server )

With the tie between our React app and our GraphQL set, it is time to start querying the database and displaying the data in the browser.

有了React应用程序和GraphQL集之间的纽带,是时候开始查询数据库并在浏览器中显示数据了。

Update the src/App.js to setup a query:

更新src/App.js以设置查询:

import React, { Component } from 'react';

// Import GraphQL helpers
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';

// App component styles
import './App.css';

class App extends Component {
  state = {
    from: 'anonymous',
    content: ''
  };
  componentDidMount() {
    // Get username form prompt
    // when page loads
    const from = window.prompt('username');
    from && this.setState({ from });
  }
  render() {
    // Coming up next
  }
}

const ALL_CHATS_QUERY = gql`
  query AllChatsQuery {
    allChats {
      id
      createdAt
      from
      content
    }
  }
`;

export default graphql(ALL_CHATS_QUERY, { name: 'allChatsQuery' })(App);

Let's breakdown what is going on here:

让我们分解一下这里发生的事情:

  • We first import graphql and gql. This libraries will help setup and parse the query respectively.

    我们首先导入graphqlgql 。 该库将分别帮助设置和解析查询。
  • At the end of the component class definition, we create the query. It looks exactly like what we did in the playground but wrapped with the gql method using template tagging.

    在组件类定义的末尾,我们创建查询。 看起来就像我们在操场上所做的一样,但是使用模板标记将其包裹在gql方法中。
  • The graphql HOC is then used to expose the result of this query to the App component's props.

    然后使用graphql HOC将查询结果公开给App组件的props。

You can now render the query in the DOM through the props:

现在,您可以通过props在DOM中呈现查询:

// Chatbox UI component
import Chatbox from './components/Chatbox';

    class App extends Component {

      //...

      render() {
        const allChats = this.props.allChatsQuery.allChats || [];
        return (
          <div className="">
            <div className="container">
              <h2>Chats</h2>
              {allChats.map(message => (
                <Chatbox key={message.id} message={message} />
              ))}
            </div>
          </div>
        );
      }
    }

    // ...

    export default graphql(ALL_CHATS_QUERY, { name: 'allChatsQuery' })(App);

The render method iterates over each of the query results and prints them to the screen using the Chatbox component. This is what the component looks like in components/Chatbox.js:

render方法遍历每个查询结果,并使用Chatbox组件将它们打印到屏幕上。 这是components/Chatbox.jscomponents/Chatbox.js

import React from 'react';
import './Chatbox.css'
    const Chatbox = ({message}) => (
      <div className="chat-box">
        <div className="chat-message">
          <h5>{message.from}</h5>
          <p>
            {message.content}
          </p>
        </div>
      </div>
    );
    export default Chatbox;

You can refer to the repo to get the basic styles for Chatbox.css and App.css. Here is what the current output should look like when you run:

您可以参考该Chatbox.css以获取Chatbox.cssApp.css的基本样式。 运行时,当前输出应如下所示:

Remember I added some data via the playground which is why I have some messages in the view.

记住,我是通过游乐场添加了一些数据的,这就是为什么在视图中有一些消息的原因。

创建新消息 ( Creating New Messages )

Mutations in GraphQL are used to create, edit, and delete values from your database. Where a query is the R (Read) in CRUD, a mutation can be the CUD (Create, Update, Delete). We are already able to read existing messages in our chat app. Next thing we should worry about is creating those messages from our React app.

GraphQL中的突变用于创建,编辑和删除数据库中的值。 如果查询是CRUD中的R(读取),则突变可以是CUD(创建,更新,删除)。 我们已经能够阅读聊天应用程序中的现有消息。 接下来我们要担心的是从React应用程序创建这些消息。

Just like the way we created a query, we can also create a mutation:

就像我们创建查询的方式一样,我们也可以创建一个突变:

import { graphql, compose } from 'react-apollo';

    // App component ...

    const CREATE_CHAT_MUTATION = gql`
      mutation CreateChatMutation($content: String!, $from: String!) {
        createChat(content: $content, from: $from) {
          id
          createdAt
          from
          content
        }
      }
    `;

    export default compose(
      graphql(ALL_CHATS_QUERY, { name: 'allChatsQuery' }),
      graphql(CREATE_CHAT_MUTATION, { name: 'createChatMutation' })
    )(App);

The mutation is a lot like a query but receives parameters -- content and from. We need to wrap the App component with both this mutation and our existing query. For this reason, we also import the compose method and use this method to wrap both HOCs.

变异很像查询,但接收参数-内容和来源。 我们需要使用此突变和现有查询来包装App组件。 因此,我们还导入了compose方法,并使用此方法包装两个HOC。

In the render method, we can have an input element that collects these message contents:

在render方法中,我们可以有一个input元素来收集这些消息内容:

render() {
   const allChats = this.props.allChatsQuery.allChats || [];
       return (
          <div className="">
            <div className="container">
              <h2>Chats</h2>
              {allChats.map(message => (
                <Chatbox key={message.id} message={message} />
              ))}

              {/* Message content input */}
              <input
                value={this.state.content}
                onChange={e => this.setState({ content: e.target.value })}
                type="text"
                placeholder="Start typing"
                onKeyPress={this._createChat}
              />
            </div>
          </div>
        );
      }
    }

The input triggers an event at every keyPress, and this event calls the _createChat method. Here is the definition of this method in the App class:

输入在每个keyPress处触发一个事件,此事件调用_createChat方法。 这是App类中此方法的定义:

_createChat= async e => {
     if (e.key === 'Enter') {
       const { content, from } = this.state;
        await this.props.createChatMutation({
          variables: { content, from }
        });
        this.setState({ content: '' });
      }
    };

We only want to run the mutation when the enter key is pressed. Notice how the createChatMutation is also exposed on the props and the variable is passed in as an object to the mutation method.

我们只想在按下回车键时运行突变。 请注意, createChatMutation也如何在props上公开,并将变量作为对象传递给mutation方法。

When you send a new message, nothing happens until you reload:

当您发送新消息时,直到重新加载,一切都不会发生:

This calls for realtime updates.

这需要实时更新。

设置实时订阅 ( Setting up Realtime Subscriptions )

Subscriptions in GraphQL are used to listen for changes by connected clients. These clients can act on these changes accordingly. Probably by updating the user interface with the changed data or even sending push notifications. The underlying technology that powers subscriptions is the well known WebSockets.

GraphQL中的订阅用于侦听已连接客户端的更改。 这些客户可以相应地对这些更改采取行动。 可能是通过使用更改后的数据更新用户界面,甚至发送推送通知。 支持订阅的基础技术是众所周知的WebSocket。

Add this method to the App class to setup a subscription:

将此方法添加到App类中以设置订阅:

_subscribeToNewChats = () => {
      this.props.allChatsQuery.subscribeToMore({
          document: gql`
            subscription {
              Chat(filter: { mutation_in: [CREATED] }) {
                node {
                  id
                  from
                  content
                  createdAt
                }
              }
            }
          `,
          updateQuery: (previous, { subscriptionData }) => {
            const newChatLinks = [
              ...previous.allChats,
              subscriptionData.data.Chat.node
            ];
            const result = {
              ...previous,
              allChats: newChatLinks
            };
            return result;
          }
        });
      };

The allChatsQuery exposes a subscribeToMore method. Once this method is called, it opens up a channel for realtime communication. The method takes an object which we can define the query document and an updateQuery method.

所述allChatsQuery公开一个subscribeToMore方法。 调用此方法后,它将打开一个用于实时通信的通道。 该方法带有一个可以定义查询文档的对象和一个updateQuery方法。

The document defines a subscription and listens for when a mutation occurs on the Chat entity before triggering an event. The update method receives the old and new value, and we are using these new value to update the old value.

该文档定义了一个订阅,并在触发事件之前侦听Chat实体上何时发生了突变。 update方法接收旧值和新值,并且我们正在使用这些新值来更新旧值。

You can kick off this subscription in the componentDidMount lifecycle method:

您可以在componentDidMount生命周期方法中开始此订阅:

this._subscribeToNewChats();

Now run once more, and you should have a running chat app:

现在再次运行,您应该有一个正在运行的聊天应用程序:

进阶学习 ( Further Learning )

You already learned a lot, but there is more to GraphQL. You can dig our Scotch articles on GraphQL to discover more. The How to GraphQL tutorials are also excellent and walks you through steps on learning about the GraphQL fundamentals. Feel free to hit the comments section if you run into issues or find a bug.

您已经学到了很多,但是GraphQL还有更多内容。 您可以在GraphQL上浏览我们的Scotch文章以发现更多内容。 How to GraphQL教程也非常出色,它引导您逐步了解GraphQL基础。 如果您遇到问题或发现错误,请随时点击评论部分。

翻译自: https://scotch.io/tutorials/how-to-build-a-realtime-chat-app-with-react-and-graphql

react中使用构建缓存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值