GraphQL 进阶: Apollo Client 之 GraphQL Subscription 和 graphql容器

前端之家收集整理的这篇文章主要介绍了GraphQL 进阶: Apollo Client 之 GraphQL Subscription 和 graphql容器前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

概述

下面是一个视频和一个GIF动画,感受一下基于Websocket,通过GraphQL实现的即时聊天应用是个什么鬼.

视频连接: https://v.qq.com/x/page/x0508...

GIF动画

graphql() 函数是一个给组件增加数据逻辑(查询,修改,删除)的一个高阶函数,存在于 react-apollo 模块中,如果要使用它,需要把它 import 进来.

函数接受一个React组件,同时返回一个经过修改(增加数据逻辑)的React组件. 属于设计模式中的装饰器模式,在不修改原组件的情况下,对组件增加额外的功能,实现了「对修改关闭,对扩展开放」的软件工程原则.

graphql 容器的基本形态如下:

# 导入 graphql 函数
import { graphql } from 'react-apollo';
# 函数签名,参数分别为GraphQL查询(通过gql 模板标签进行构造,一个可选的配置对象,以及一个被包装的React组件)
graphql(query,[config])(component)

graphql() 函数有两个参数

第一个参数为通过 gql 包裹的查询字符串,如:

const TODO_QUERY = gql`query Todo {
  todos: {
    id
    text
  }
`}

第二个参数为一个配置对象,方括号表示其是可选的,可省略
第三个参数为被包装的React组件.

graphql() 函数react-apollo 提供的最重要的一个函数. 用这个函数可以创建执行查询何更新的高阶组件.

graphql() 函数可以这样用:

function TodoApp({ data: { todos } }) {
  return (
    <ul>
      {todos.map(({ id,text }) => (
        <li key={id}>{text}</li>
      ))}
    </ul>
  );
}
export default graphql(gql`
  query TodoAppQuery {
    todos {
      id
      text
    }
  }
`)(TodoApp);

也可以定义中间函数

# 中间函数
const withTodoAppQuery = graphql(gql`query { ... }`);
# 传入React组件给这个中间函数
const TodoAppWithData = withTodoAppQuery(TodoApp);
# 导出这个组件
export default TodoAppWithData;

graphql() 函数也可以作为装饰器使用:

@graphql(gql`
  query TodoAppQuery {
    todos {
      id
      text
    }
  }
`)
export default class TodoApp extends Component {
  render() {
    const { data: { todos } } = this.props;
    return (
      <ul>
        {todos.map(({ id,text }) => (
          <li key={id}>{text}</li>
        ))}
      </ul>
    );
  }
}

graphql() 函数的使用依赖于在React组件树的根外层再包装一个 <ApolloProvider/> 组件. <ApolloProvider /> 在其属性上提供了一个 ApolloClient 实例用于访问数据.

通过 graphql() 函数增强的组件依据GraphQL的查询类型(Query,Mutation,Subscription)有不同的行为.

配置对象

config.options

options 该对象可以是一个纯对象,或者是一个函数. 用于定制GraphQL查询的行为,比如,给一个Mutation传递输入对象参数:

options: ({ params }) => ({
  variables: {
    text: '我是一个粉刷匠,粉刷本领强4'
  },}),

纯对象很简单,形式为:

const config = {
  name: 'getTodos'
}

options 函数,接收一个组件属性作为参数,形式为:

export default graphql(TODO_QUERY,{
  options: (props) => ({
  }),})(MyComponent);

config.props

属性用于定义一个映射函数. 传入组件自身的属性,和通过 graphql() 函数添加属性(Query为,props.data,Mutation 为 props.mutate),这让我们能够构造一个新的属性对象,并把这个新的属性对象传递给被graphql()包装的组件.

config.props 的基本用途

  • config.props 可以让我们把复杂的函数调用抽离成单独的模块,并且作为简单的属性传递给组件.

  • 从UI组件解耦 GraphQL 逻辑,让UI组件更简单,大体上讲就是UI组件只负责UI的渲染,config.props 选项用于封装复杂的数据处理逻辑. UI组件和数据交互逻辑实现分离,并且通过 config.props 关联.

config.name

这选项的作用是避免一个组件中有多个GraphQL查询,或者Mutation名称上的冲突.

我们方位GraphQL查询结果的数据通常是通过this.props.data返回数据的,data 属性是通过graphql() 高阶函数注入到我们的组件属性中的,这个名字有时候会和我们组件本身属性名称冲突,为了解决冲突问题,可以在graphql()函数第二个参数配置对象中设置一个 name,然后通过 this.props.${name} 来访问这个属性.

export default compose(
  graphql(gql`mutation (...) { ... }`,{ name: 'createTodo' }),graphql(gql`mutation (...) { ... }`,{ name: 'updateTodo' }),{ name: 'deleteTodo' }),)(MyComponent);
function MyComponent(props) {
  console.log(props.createTodo);
  console.log(props.updateTodo);
  console.log(props.deleteTodo);
  return null;

这样,我们就可以通过 this.props.createTodo,this.props.updateTodo,this.props.deleteTodo 来访问我们需要的数据了.

config.withRef

设置 config.withRef 为 true 时,可以通过graphql() 返回的高阶组件上调用 getWrappedInstance 方法获取被包装组件的实例. 通常我们需要访问被包装组件属性方法是需要把这个选项设置为true,默认为false

# 创建一个UI组件

class HelloWorld extends Component {
  saySomething() {
    console.log('Hello,world!');
  }
  render() {
  }
}

# 添加数据逻辑,编程一个支持GraphQL的组件,我们简称GraphQL组件

const HelloWorldWithData = graphql(
  gql`{ ... }`,{ withRef: true },)(HelloWorld);

# 使用 GraphQL 组件
# 通过该组件的ref属性,我们能够访问到原始的 HelloWorld 组件实例

class Container extends Component {
  render() {
    return (
      <HelloWorldWithData ref={component => {
          assert(component.getWrappedInstance() instanceof HelloWorld);
          component.saySomething();
        }}
      />
    );
  }
}

config.alias

组件别名,主要是给 React Devtools 使用,用于区分多个不同的高阶组件

export default compose(
  graphql(gql`{ ... }`,{ alias: 'withCurrentUser' }),graphql(gql`{ ... }`,{ alias: 'withList' }),)(MyComponent);

看一下别名的效果,我们实际的组件名称FeedbackList,通过graphql()高阶组件包装后的组件名称Apollo(FeedbackList),如果组件不多的情况下,我们很好分辨不同的组件,如果一个单页应用中使用了大量的graphql()高阶组件,这样的名字容易引其混乱,因此我们通过alias能够避免名称上的混乱,让组件更容易识别. 下面我们看一下代码:

import React,{ Component } from 'react'
import PropTypes from 'prop-types'
import { graphql,gql,withApollo,compose } from 'react-apollo'

// 查询文本
import QUERY_FeedBACKS from './graphql/ListFeedback.graphql'
import SUBSCRIPTION_NEW_FeedBACKS from './graphql/SubscribeAddFeedback.graphql'

// 反馈列表组件
class FeedbackList extends Component {
  // constructor(props) {
  //   super(props)
  // }
  componentWillMount() {
    this.props.subscribeToNewFeedback();
  }
  render() {
    if(this.props.data.loading == true) {
      return <div>Loading...</div>
    } else {
      return (
        <ul>{
          this.props.data.Feedbacks.map((item,index) => {
            console.log(item.id)
            return (
              <div key={item.id}>{item.text}</div>
            )
          })
        }</ul>
      )
    }
  }
}
// 属性验证
FeedbackList.propTypes = {
  subscribeToNewFeedback: PropTypes.func.isrequired
}
// 高阶组件
export default graphql(QUERY_FeedBACKS,{
  // name: 'FeedbackList',options: ({ params }) => ({
    variables: {
      key: 'value'
    },// 无别名时的效果
  // alias: 'FeedbackListWithData' 
})(FeedbackList);

无别名

增加别名后

代码

本文所描述的代码放在Github上,可以Clone下来进行学习和测试,代码中的数据是通过一个内存数组存储的,服务器重启后数据丢失. 如果需要持久化,可以改为使用数据库.

示例代码实现了GraphQL的订阅模式,客户端通过 Websocket 建立到服务器的长连接. 可以一次作为「使用GraphQL实现即时聊天应用」的基础,示例代码包含完整的服务器和客户端代码,可通过下面两行命令启动服务器和客户端.

# 启动GraphQL服务器 
# GraphQL服务器,提供GraphQL查询接口: http://localhost:7001/api
# 订阅服务器,订阅功能: http://localhost:7003/Feedback

yarn server 

# 启动 webpack dev server,提供Web界面: http://localhost:7001

yarn client

GraphiQL 查询工具,可以通过 http://localhost:7001/graphiql 访问. 启动服务器和客户端后,可以通过在 GraphiQL 工具中执行如下的查询看到效果:

查询

mutation AddFeedback($data: FeedbackInput!) {
  addFeedback(data: $data) {
    id
    text
  }
}

变量

{
  "data": {
    "text": "我是一个粉刷匠,粉刷本领强"
  }
}

这个连接是订阅

http://localhost:7001/graphiq...

这个连接是添加一条反馈(Feedback)记录,以及对应的变量. 执行此Mutation,会在客户端和订阅窗口看到数据的实时更新.

http://localhost:7001/graphiq...

参考资料

http://dev.apollodata.com/rea...
https://github.com/apollograp...
https://github.com/apollograp...
http://dev.apollodata.com/rea...
https://webpack.js.org/config...

猜你在找的React相关文章