# hackernews-react-apollo
**Repository Path**: custer_git/hackernews-react-apollo
## Basic Information
- **Project Name**: hackernews-react-apollo
- **Description**: 学习:https://www.howtographql.com
- **Primary Language**: JavaScript
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: https://news.ycombinator.com/news
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2017-08-20
- **Last Updated**: 2022-06-12
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# hackernews-react-apollo
:fire: [学习](https://www.howtographql.com)
[效果](https://news.ycombinator.com/news) :fire:
### :rocket: [安装和初始化](#part1)
### :rocket: [Query: 查询价值Link](#part2)
### :rocket: [Mutation: CreateLink](#part3)
### :rocket: [Routing](#part4)
### :rocket: [Authentication 认证](#part5)
### :rocket: [More Mutations and Updating the Store](#part6)
### :rocket: [Filtering: Searching the List of Links](#part7)
### :rocket: [Realtime Updates with GraphQl](#part8)
### :rocket: [Pagination](#part9)
### :rocket: [Summary](#part10)
## :checkered_flag: 安装和初始化
### 工具安装 create-react-app graph cool yarn
我们需要在命令行中安装 create-react-app 工具,graphcool工具,你可能还需要安装 yarn。
```
$ npm install -g create-react-app yarn graphcool
```
yarn 命令是
```
yarn global add create-react-app graph cool
```
### 创建GraphQL Server
```
graphcool init --schema https://graphqlbin.com/hn-starter.graphql --name Hackernews
```
浏览器会打开一个页面,需要登录,进入之后,页面显示
> Your project is ready
Successfully authenticated. You can now close this tab.
You'll find your GraphQL endpoints in your terminal.

Creating project Hackernews... 创建成功后,目录下有个文件夹 _project.graphcool_
### 新建Frontend项目
$ create-react-app hackernews-react-apollo
工具会自动初始化一个脚手架并安装 React 项目的各种必要依赖,如果在过程中出现网络问题,请尝试配置代理或使用其他 npm registry。
然后我们进入项目并启动。
$ cd hackernews-react-apollo
$ yarn start
此时浏览器会访问 http://localhost:3000/ ,看到 Welcome to React 的界面就算成功了。
把project.graphcool 文件夹拷贝到 hackernews-react-apollo 目录下
然后清理下目录结构如下图,方便开发:
```
.
├── README.md
├── node_modules
├── project.graphcool
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
├── src
│ ├── App.test.js
│ ├── components
│ │ └── App.js
│ ├── index.js
│ ├── logo.svg
│ ├── registerServiceWorker.js
│ └── styles
│ ├── App.css
│ └── index.css
└── yarn.lock
```
### 准备样式
在 public/index.html 中新建一个 link
```
```
打开 index.css 替换成下面的代码
```
body {
margin: 0;
padding: 0;
font-family: Verdana, Geneva, sans-serif;
}
input {
max-width: 500px;
}
.gray {
color: #828282;
}
.orange {
background-color: #ff6600;
}
.background-gray {
background-color: rgb(246,246,239);
}
.f11 {
font-size: 11px;
}
.w85 {
width: 85%;
}
.button {
font-family: monospace;
font-size: 10pt;
color: black;
background-color: buttonface;
text-align: center;
padding: 2px 6px 3px;
border-width: 2px;
border-style: outset;
border-color: buttonface;
cursor: pointer;
max-width: 250px;
}
```
### 安装 react-apollo
```
yarn add react-apollo
```
下面就可以开始编写代码了
### 配置 ApolloClient
```
import React from 'react';
import ReactDOM from 'react-dom';
import './styles/index.css';
import App from './components/App';
import registerServiceWorker from './registerServiceWorker';
// 导入需要的模块
import { ApolloProvider, createNetworkInterface, ApolloClient } from 'react-apollo'
/**
* Apollo Client有一个可插拔的网络接口层,可以配置网络接口 使用 createNetworkInterface
* 修改GraphQL服务器的URL,创建自定义的NetworkInterface
*
* Query batching:当多个请求在一个特定的时间间隔内产生时(比如:100毫秒内)
* Apollo会把多个查询组合成一个请求,
* 比如在渲染一个包含导航条,边栏,内容等带有GraphQL查询的组件时
* 使用Query batching,要传递BatchedNetworkInterface给ApolloClient构造函数
*/
const networkInterface = createNetworkInterface({
uri: '__SIMPLE_API_ENDPOINT__'
})
/**
* 实例化ApolloClient - 默认情况客户端会发送到相同主机名(域名)下的/graphql端点
* 查询去除重复(Query deduplication)
* 查询去除重复可以减少发送到服务器的查询数量,默认关闭
* 通过queryDeduplication选项传递给ApolloClient构造函数开启
* 查询去重在多个组件显示相同数据的时候非常有用,避免从服务器多次获取相同的数据
*/
const client = new ApolloClient ({
networkInterface
})
/* 挂载组件 - 要连接客户端到React组件树,要确保ApolloProvider作为一个容器去包裹
其他的需要访问GraphQL服务器数据的React组件 */
ReactDOM.render(
, document.getElementById('root')
)
registerServiceWorker()
```
打开终端,在 project.graphcool的目录下,运行
```
graphcool endpoints
```
拷贝 Simple API 粘贴到 src/index.js 替换 __SIMPLE_API_ENDPOINT__
这样就可以开始加载数据到你的App里面了。
## Query : 查询价值Link
### 准备 React components
第一步就是书写简单的单个链接Link组件,在目录src/compinents/下创建一个新的文件Link.js
```
import React, { Component } from 'react'
class Link extends Component {
render(){
return(
{this.props.link.description} ({this.props.link.url})
)
}
_voteForLink = async() => {
// ...将在第六章实现这里的代码
}
}
export default Link
```
下面实现渲染links列表的组件, 新建src/components/LinkList.js文件
```
import React, { Component } from 'react'
import Link from './Link'
class LinkList extends Component {
render(){
const linksToRender = [{
id: '1',
description: 'The Coolest GraphQL Backend 😎',
url: 'http://www.graph.cool'
}, {
id: '2',
description: 'The Best GraphQL Client',
url: 'http://dev.apollodata.com/'
}]
return(
{linksToRender.map(link => (
))}
)
}
}
export default LinkList
```
把组件挂载到App.js
```
import React, { Component } from 'react';
import LinkList from './LinkList'
class App extends Component {
render() {
return (
);
}
}
export default App;
```
现在网页上有两条你刚才添加的数据,想把它变成后台加载的数据,还要等一会,加油
### 写 GrapQL Query 查询语句
想要从数据库加载数据 链接,第一件事情就是你需要定义GraphQL查询语句
像这样语法格式:
```
query AllLinks {
allLinks {
id
createdAt
description
url
}
}
```
### 使用 Apollo Client 查询
当使用Apollo的时候,你有两种方式发送需要查询的语句到服务端
第一种是在ApolloClient直接使用 query 方法
一个实际的例子像下面的代码:
```
client.query({
query: gql`
query AllLinks {
allLinks {
id
}
}
`
}).then(response => console.log(response.data.allLinks))
```
打开 LinkList.js 文件,在底部书写查询语句,并且替换 export default LinkList
```
const ALL_LINKS_QUERY = gql`
query AllLinksQuery {
allLinks {
id
createdAt
url
description
}
}
`
export default graphql(ALL_LINKS_QUERY, { name: 'allLinksQuery' })(LinkList)
```
对于上面的代码,你需要导入相对应的库
```
import { graphql, gql } from 'react-apollo'
```
上面就是全部你查询数据的语句,现在可以把之前的渲染语句删除,替换成真正的从服务端取的数据
更新 LinkList.js 中的 render 代码, 直接展示本节课的 LinkList.js 的全部代码
```
import React, { Component } from 'react'
import Link from './Link'
import { graphql, gql } from 'react-apollo'
class LinkList extends Component {
render(){
if (this.props.allLinksQuery && this.props.allLinksQuery.loading) {
return Loading
}
if(this.props.allLinksQuery && this.props.allLinksQuery.error){
return Error
}
const linksToRender = this.props.allLinksQuery.allLinks
return(
{linksToRender.map(link => (
))}
)
}
}
const ALL_LINKS_QUERY = gql`
query AllLinksQuery {
allLinks {
id
createdAt
url
description
}
}
`
export default graphql(ALL_LINKS_QUERY, { name: 'allLinksQuery' })(LinkList)
```
此时,如果你没有向服务端添加数据,页面显示是空的,现在让我们往后台添加数据吧:
在命令行,cd 到 hackernews-react-apollo 目录下 运行
```
graphcool playground
```
此时浏览器会打开 https://console.graph.cool/, 我们在左侧 Data 中找到 Link,点击进入添加数据
左侧下方 点击 PLAYGROUND 按钮,把我们的查询代码拷贝进去
```
query AllLinksQuery {
allLinks {
id
createdAt
url
description
}
}Ï
```
点击运行就可以查看到刚才添加的数据,此时,返回到前端页面,刷新页面,就可以看到后端传递过来的数据
## Mutation: CreateLink
准备一个新的文件 src/components/CreateLink.js,书写下面的代码
```
import React, { Component } from 'react'
class CreateLink extends Component {
state = {
description: '',
url: ''
}
render(){
return(
)
}
_createLink = async () => {
// ...等会实现
}
}
export default CreateLink
```
简单的书写两个 input 表单,提供 url 和 description。
### 书写 Mutation
第一步:定义 mutation,并且使用 graphql,在上面的代码底部添加下面的语句,并替换export default CreateLink
```
const CREATE_LINK_MUTATION = gql`
mutation CreateLinkMutation($description: String!, $url: String!) {
createLink(
description: $description,
url: $url,
) {
id
createdAt
url
description
}
}
`
export default graphql(CREATE_LINK_MUTATION, { name: 'createLinkMutation' })(CreateLink)
```
在运行之前,在文件头部导入需要的模块
```
import { graphql, gql } from 'react-apollo'
```
实现_createLink:
```
_createLink = async () => {
const { description, url } = this.state
await this.props.createLinkMutation({
variables: {
description,
url
}
})
}
```
下面在 App.js 中 render 刚书写的 CreateLink 组件
```
import React, { Component } from 'react';
import LinkList from './LinkList'
import CreateLink from './CreateLink'
class App extends Component {
render() {
return (
);
}
}
export default App;
```
现在保存代码,刷新页面,会出现 input 框,填写,提交之后,刷新页面,会有刚才提交的信息
## Routing
本节课内容是学习使用 react-router 和 Apollo 实现导航栏功能
### 首先安装依赖的模块
```
yarn add react-router react-router-dom
```
### 创建一个 Header
创建一个新的头部组件,用户可以从这里完成跳转到你 app 的各个部分
src/components/Header.js
```
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import { withRouter } from 'react-router'
class Header extends Component {
render(){
return(
)
}
}
export default withRouter(Header)
```
这里简单的渲染了两个 Link 组件,可以方便用户跳转到 LinkList 和 CreateLink 组件
### 设置 routes
在根组件 App 下配置不同的 routes。打开 App.js,更新 render 代码:
```
import React, { Component } from 'react';
import LinkList from './LinkList'
import CreateLink from './CreateLink'
import Header from './Header'
import { Switch, Route } from 'react-router-dom'
class App extends Component {
render() {
return (
);
}
}
export default App;
```
最后更新 index.js 代码:
```
[ ... ]
import { BrowserRouter } from 'react-router-dom'
[ ... ]
ReactDOM.render(
, document.getElementById('root')
)
registerServiceWorker()
```
### 实现导航
需要在执行 Mutation 之后,实现从 CreateLink 到 LinkList 的自动重定向
打开 CreateLink.js 文件,书写 _createLink 代码:
```
_createLink = async () => {
const { description, url } = this.state
await this.props.createLinkMutation({
variables: {
description,
url
}
})
this.props.history.push('/')
}
```
这样就完成了在CreateLink之后自动跳转到LinkList页面
## Authentication 认证
使用 Apollo 和 Graphcoll 实现认证功能完成用户的登录
### 准备 React 相关组件
先简单实现一个初等级的 Login 组件, src/component/Login.js
```
import React, { Component } from 'react'
import { GC_USER_ID, GC_AUTH_TOKEN } from '../constants'
class Login extends Component {
state = {
login: true, // 在 login 和 signup 之间切换
email: '',
password: '',
name: ''
}
render(){
return(
)
}
_confirm = async () => {
// 一会来实现
}
_saveUserData = (id, token) => {
localStorage.setItem(GC_USER_ID, id)
localStorage.setItem(GC_AUTH_TOKEN, token)
}
}
export default Login
```
让我们快速理解下新组建的结构,它有两个主要的 state
一个 state 是为了用户已经拥有账户,只需要直接登录,在这个 state,组件组件仅仅 render 两个 input 框,给用户提供 email 和 password。注意在这种情况下 state.login 的值是 true
第二个 state 是用户还没有创建账户,因此需要 sign up,这里你需要 render 第三个 input 框,用户可以输入name。在这种情况下 state.login 的值是 false
_confirm 方法将被是用来实现 mutation,我们需要使用 mutation 向后台发送 login 信息
下一步,你也需要提供 constants.js 文件,我们用来定义 keys 来认证,我们在浏览器 localStorage 存储的 JWT TOKEN
在 src/ 下创建 constants.js
```
export const GC_USER_ID = 'graphcool-user-id'
export const GC_AUTH_TOKEN = 'graphcool-auth-token'
```
下一步打开 App.js 更新 route
```
import React, { Component } from 'react';
import { Switch, Route } from 'react-router-dom'
import LinkList from './LinkList'
import CreateLink from './CreateLink'
import Header from './Header'
import Login from './Login'
class App extends Component {
render() {
return (
);
}
}
export default App;
```
更新 Header.js, 把 Link 加入代码,用户可以直接在导航栏跳转
```
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import { withRouter } from 'react-router'
import { GC_USER_ID, GC_AUTH_TOKEN } from '../constants'
class Header extends Component {
render() {
const userId = localStorage.getItem(GC_USER_ID)
return (
Hacker News
new
{userId &&
}
{userId ?
{
localStorage.removeItem(GC_USER_ID)
localStorage.removeItem(GC_AUTH_TOKEN)
this.props.history.push(`/new/1`)
}}>logout
:
login
}
)
}
}
export default withRouter(Header)
```
在 Login.js 中实现认证功能之前,需要准备 Graphcool 项目,并在服务端启用身份验证
### 启用 Email-和-Password 验证 && 更新 Schema
在终端文件 project.graphcool 的目录下,输入下面的命令
```
graphcool console
```
将会打开 Graphcool Console,一套 Web UI,允许你配置你的 Graphcool poject
选择左侧的 integrations 点击 email-password-auth-integration
完成之后,在终端 project.graphcool 的目录下,输入下面的命令
```
graphqlcool pull
```
选择yes,然后在 Graphcool Console WebUI中修改更新 User 和 Link type
```
type Link implements Node {
url: String!
description: String!
createdAt: DateTime!
id: ID! @isUnique
updatedAt: DateTime!
postedBy: User @relation(name: "UsersLinks")
}
type User implements Node {
createdAt: DateTime!
email: String @isUnique
id: ID! @isUnique
password: String
updatedAt: DateTime!
name: String!
links: [Link!]! @relation(name: "UserLinks")
}
```
在 Link 中增加
```
postedBy: User @relation(name: "UsersLinks")
```
在 User 中增加
```
name: String!
links: [Link!]! @relation(name: "UserLinks")
```
保存并在终端运行
```
graphcool status
```
查看状态,如果是在WebUI上修改的,应该 graphcool pull 同步到本地
如果是在project file project.graphcool 修改的,应该 graphcool push 同步到服务器
### 实现 Login Mutation
createUser 和 signinUser 是两个常规的 GraphQL mutations
打开 Login.js 在文件底部加上这两个 mutation 的定义,同时替换 export defautl
```
[ ... ]
import { gql, graphql, compose } from 'react-apollo'
[ ... ]
const CREATE_USER_MUTATION = gql`
mutation CreateUserMutation($name: String!, $email: String!, $password: String!) {
createUser(
name: $name,
authProvider: {
email:{
email: $email,
password: $password
}
}
){
id
}
signinUser(email:{
email: $email,
password: $password
}){
token
user {
id
}
}
}
`
const SIGNIN_USER_MUTATION = gql`
mutation SigninUserMutation($email: String!, $password: String!) {
signinUser(email:{
email: $email,
password: $password
}) {
token
user{
id
}
}
}
`
export default compose(
graphql(CREATE_USER_MUTATION, { name: 'createUserMutation' }),
graphql(SIGNIN_USER_MUTATION, { name: 'signinUserMutation' }),
)(Login)
```
react-apollo 导出一个 compose 函数,用于减少书写代码的量
在 Login.js 中实现 _confirm 的代码:
```
_confirm = async () => {
const { name, email, password } = this.state
if (this.state.login) {
const result = await this.props.signinUserMutation({
variables: {
email,
password
}
})
const id = result.data.signinUser.user.id
const token = result.data.signinUser.token
this._saveUserData(id, token)
}else{
const result = await this.props.createUserMutation({
variables: {
name,
email,
password
}
})
const id = result.data.signinUser.user.id
const token = result.data.signinUser.token
this._saveUserData(id, token)
}
this.props.history.push(`/`)
}
```
代码非常的直接,如果用户只是想登录,调用 signinUserMutation 传入需要的参数 email 和 password
另一方面,如果用户想要创建用户 createUserMutation 还要另外传参 name。
之后保存 id 和 token 在 localStorage,并导航到 根路由
这时候就可以创建用户 完成之后自动跳转到根目录
### 更新 createLink Mutation
重新定义 `CREATE_LINK_MUTATION`:
```
const CREATE_LINK_MUTATION = gql`
mutation CreateLinkMutation($description: String!, $url: String!, $postedById: ID!) {
createLink(
description: $description,
url: $url,
postedById: $postedById
) {
id
createdAt
url
description
postedBy{
id
name
}
}
}
`
```
更新 _createLink 代码:
```
import { GC_USER_ID } from '../constants'
[ ... ]
_createLink = async () => {
const postedById = localStorage.getItem(GC_USER_ID)
if(!postedById) {
console.error('用户没有登录')
return
}
const { description, url } = this.state
await this.props.createLinkMutation({
variables: {
description,
url,
postedById
}
})
this.props.history.push('/')
}
[ ... ]
```
### 配置 Apollo with Auth Token
在index.js中书写 middleware 部分的代码
```
import { GC_AUTH_TOKEN } from './constants'
[ ... ]
networkInterface.use([{
applyMiddleware(req, next){
if(!req.options.headers){
req.options.headers = {}
}
const token = localStorage.getItem(GC_AUTH_TOKEN)
req.options.headers.authorization = token ? `Bearer ${ token }` : null
next()
}
}])
```
到这里,就可以注册、登录、登录之后的创建新的链接
## More Mutations and Updating the Store
将实现的下一个功能是 `投票功能`。登录的用户可以投票,最高支持的链接将会在单独的路由地址显示
### 准备 React Component 组件
打开 Link.js 更新 render 代码:
```
render(){
const userId = localStorage.getItem(GC_USER_ID)
return(
{this.props.index + 1}.
{userId &&
this._voteForLink()}>👍
}
{this.props.link.description} ({this.props.link.url})
{this.props.link.votes.length} votes | by {this.props.link.postedBy ? this.props.link.postedBy.name : 'Unknown'} {timeDifferenceForDate(this.props.link.createdAt)}
)
}
```
这样就准备好了 Link 组件来呈现每个链接的投票数以及发布它的名称。
注意:你使用了 `timeDifferenceForDate` 函数,该函数将时间转换成用户友好的字符串,例如‘3小时前’
在 src下创建 utils.js 来完成函数 `timeDifferenceForDate` 的实现
```
function timeDifference(current, previous) {
const milliSecondsPerMinute = 60 * 1000
const milliSecondsPerHour = milliSecondsPerMinute * 60
const milliSecondsPerDay = milliSecondsPerHour * 24
const milliSecondsPerMonth = milliSecondsPerDay * 30
const milliSecondsPerYear = milliSecondsPerDay * 365
const elapsed = current - previous
if(elapsed < milliSecondsPerMinute / 3) {
return '刚刚'
}
if(elapsed < milliSecondsPerMinute) {
return '一分钟前'
}
else if(elapsed < milliSecondsPerHour) {
return Math.round(elapsed / milliSecondsPerMinute) + '分钟前'
}
else if(elapsed < milliSecondsPerDay) {
return Math.round(elapsed / milliSecondsPerHour) + '小时前'
}
else if(elapsed < milliSecondsPerMonth) {
return Math.round(elapsed / milliSecondsPerDay) + '天前'
}
else if(elapsed < milliSecondsPerYear) {
return Math.round(elapsed / milliSecondsPerMonth) + '月前'
}
else {
return Math.round(elapsed / milliSecondsPerYear) + '年前'
}
}
export function timeDifferenceForDate(date){
const now = new Date().getTime()
const updated = new Date(date).getTime()
return timeDifference(now, updated)
}
```
返回 Link.js 在文件头引入需要的模块文件
```
import { GC_USER_ID } from '../constants'
import { timeDifferenceForDate } from '../utils'
```
最后,每个 Link 元素还将在列表中显示其位置,因此要从 LinkList 组件传递一个索引
```
return(
{linksToRender.map((link, index) => (
))}
)Ï
```
现在还不能运行,因为 votes 还没有加载到查询语句中,下面来修改代码:
### 更新 Schema
上一次是在浏览器的 Web UI 中修改,然后 graphcool pull 下来到本地,这次修改 project.graphcool 文件
```
vim project.graphcool
```
```
type Link implements Node {
url: String!
description: String!
createdAt: DateTime!
id: ID! @isUnique
updatedAt: DateTime!
postedBy: User @relation(name: "UsersLinks")
votes: [Vote!]! @relation(name: "VotesOnLink")
}
type File implements Node {
contentType: String!
createdAt: DateTime!
id: ID! @isUnique
name: String!
secret: String! @isUnique
size: Int!
updatedAt: DateTime!
url: String! @isUnique
}
type User implements Node {
createdAt: DateTime!
email: String @isUnique
id: ID! @isUnique
password: String
updatedAt: DateTime!
name: String!
links: [Link!]! @relation(name: "UsersLinks")
votes: [Vote!]! @relation(name: "UsersVotes")
}
type Vote {
user: User! @relation(name: "UsersVotes")
link: Link! @relation(name: "VotesOnLink")
}
```
然后在终端运行
```
graphcool push
```
现在可以修改 LinkList.js 中的 `ALL_LINKS_QUERY` 语句
```
const ALL_LINKS_QUERY = gql`
query AllLinksQuery {
allLinks {
id
createdAt
url
description
postedBy {
id
name
}
votes {
id
user {
id
}
}
}
}
`
```
### 调用 Mutation
打开 Link.js 文件在底部添加 mutation 定义,并且替换 export Link 语句
```
const CREATE_VOTE_MUTATION = gql`
mutation CreateVoteMutation($userId: ID!, $linkId: ID!) {
createVote(userId: $userId, linkId: $linkId){
id
link {
votes{
id
user{
id
}
}
}
user {
id
}
}
}
`
export default graphql(CREATE_VOTE_MUTATION, {
name: 'createVoteMutation'
})(Link)
```
现在我们来实现 _voteForLink 功能:
```
_voteForLink = async() => {
const userId = localStorage.getItem(GC_USER_ID)
const voterIds = this.props.link.votes.map(vote => vote.user.id)
if(voterIds.includes(userId)) {
console.log(`用户 (${userId}) 已经给这个链接投过票了`)
return
}
const linkId = this.props.link.id
await this.props.createVoteMutation({
variables:{
userId,
linkId
}
})
}
```
该方法的第一步是检查当前用户是否已经为该链接投票,如果是这种情况,可以提前返回,而不是执行 mutation
现在重新运行 yarn start 就可以有点赞的功能,刷新之后,就可以看见投票个数的增加
但是仍有缺陷,投票之后不能自动刷新,所以用户可以提交无限的投票,直到页面刷新
但是至少知道 mutation 是有效的,下面,将会解决问题,并确保每次 mutation 后缓冲都会被刷新
### 更新缓存
Apollo 可以手动控缓存内容,这是非常方便的,特别是在执行 mutation 之后
修改 Link.js 文件下的 _voteForLink 函数
```
const linkId = this.props.link.id
await this.props.createVoteMutation({
variables:{
userId,
linkId
},
update: (store, {data: { createVote }}) => {
this.props.updateStroeAfterVote(store, createVote, linkId)
}
})
```
在 Link 的父组件 LinkList 实现 update 函数功能
```
[ ... ]
{linksToRender.map((link, index) => (
))}
[ ... ]
_updateCacheAfterVote = (store, createVote, linkId) => {
const data = store.readQuery({ query: ALL_LINKS_QUERY })
const votedLink = data.allLinks.find(link => link.id === linkId)
votedLink.votes = createVote.link.votes
store.writeQuery({ query: ALL_LINKS_QUERY, data })
}
[ ... ]
```
CreateLink.js 更新 createLinkMutation 在 _createLink 里面
```
[ ... ]
import { ALL_LINKS_QUERY } from './LinkList'
[ ... ]
await this.props.createLinkMutation({
variables: {
description,
url,
postedById
},
update: (store, {data: { createLink }}) => {
const data = store.readQuery({ query: ALL_LINKS_QUERY })
data.allLinks.splice(0, 0, createLink)
store.writeQuery({
query: ALL_LINKS_QUERY,
data
})
}
})
```
在 LinkList.js 中给 ALL_LINKS_QUERY 添加 export 关键字
```
export const ALL_LINKS_QUERY = gql`
query AllLinksQuery {
allLinks {
id
createdAt
url
description
postedBy {
id
name
}
votes {
id
user {
id
}
}
}
}
`
```
现在 LinkList.js 文件代码如下:
```
import React, { Component } from 'react'
import Link from './Link'
import { graphql, gql } from 'react-apollo'
class LinkList extends Component {
render(){
if (this.props.allLinksQuery && this.props.allLinksQuery.loading) {
return Loading
}
if(this.props.allLinksQuery && this.props.allLinksQuery.error){
return Error
}
const linksToRender = this.props.allLinksQuery.allLinks
return(
{linksToRender.map((link, index) => (
))}
)
}
_updateCacheAfterVote = (store, createVote, linkId) => {
const data = store.readQuery({ query: ALL_LINKS_QUERY })
const votedLink = data.allLinks.find(link => link.id === linkId)
votedLink.votes = createVote.link.votes
store.writeQuery({ query: ALL_LINKS_QUERY, data })
}
}
export const ALL_LINKS_QUERY = gql`
query AllLinksQuery {
allLinks {
id
createdAt
url
description
postedBy {
id
name
}
votes {
id
user {
id
}
}
}
}
`
export default graphql(ALL_LINKS_QUERY, { name: 'allLinksQuery' })(LinkList)
```