react+apollo+prisma入门demo搭建---6、更多的变更以及更新store

邵阳德
2023-12-01

react+apollo+prisma入门demo搭建—6、更多的变更以及更新store

根据HOW TO GRAPHQL官网的例子,做了些对最新版的改动,适合最新框架的学习。

本系列文章注重前端方面的开发,对于node方面的放在下一个系列。在此过程中有任何问题,都欢迎在评论中提问,会及时反馈

系列目录:

第一章. Frontend开始
第二章. Queries组件编写(Loading Links)
第三章. Mutations组件编写(Creating Links)
第四章. 页面路由
第五章. 身份验证
第六章. 更多的变更以及更新store

为了照顾更加全面的读者,我写的会尽量详细,熟练的开发者可进行快速选择性的阅读

更多的变更和升级store

本节我们将要完成点赞功能,经过身份验证的用户可以进行链接点赞。

准备React组件

打开Link.js,更新render为以下代码:

render() {
  const authToken = localStorage.getItem(AUTH_TOKEN)
  return (
    <div className="flex mt2 items-start">
      <div className="flex items-center">
        <span className="gray">{this.props.index + 1}.</span>
        {authToken && (
          <div className="ml1 gray f11" onClick={() => this._voteForLink()}>
            ▲
          </div>
        )}
      </div>
      <div className="ml1">
        <div>
          {this.props.link.description} ({this.props.link.url})
        </div>
        <div className="f6 lh-copy gray">
          {this.props.link.votes.length} votes | by{' '}
          {this.props.link.postedBy
            ? this.props.link.postedBy.name
            : 'Unknown'}{' '}
          {timeDifferenceForDate(this.props.link.createdAt)}
        </div>
      </div>
    </div>
  )
}

我们更新了Link组件来展示点赞数以及发布用户等更多信息。另外,如果用户当前已登录,就可以看到点赞按钮。
请注意我们使用了一个timeDifferenceForDate函数,该函数会智能的将时间戳转化为对用户更友好的字符串。
下面我们就开始书写timeDifferenceForDate函数。
在src目录下新建utils.js文件

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 'just now'
  }

  if (elapsed < milliSecondsPerMinute) {
    return 'less than 1 min ago'
  } else if (elapsed < milliSecondsPerHour) {
    return Math.round(elapsed / milliSecondsPerMinute) + ' min ago'
  } else if (elapsed < milliSecondsPerDay) {
    return Math.round(elapsed / milliSecondsPerHour) + ' h ago'
  } else if (elapsed < milliSecondsPerMonth) {
    return Math.round(elapsed / milliSecondsPerDay) + ' days ago'
  } else if (elapsed < milliSecondsPerYear) {
    return Math.round(elapsed / milliSecondsPerMonth) + ' mo ago'
  } else {
    return Math.round(elapsed / milliSecondsPerYear) + ' years ago'
  }
}

export function timeDifferenceForDate(date) {
  const now = new Date().getTime()
  const updated = new Date(date).getTime()
  return timeDifference(now, updated)
}

接着,我们在Link.js中引入该函数,以及AUTH_TOKEN

import { AUTH_TOKEN } from '../constants'
import { timeDifferenceForDate } from '../utils'

最后,为了使Link可以展示在恰当的位置,我们需要在LinkList中加入index索引

打开LinkList.js,修改render中return

return (
  <div>
    {linksToRender.map((link, index) => (
      <Link key={link.id} link={link} index={index} />
    ))}
  </div>
)

接下来我们就开始构建新的query,打开LinkList.js升级FEED_QUERY:

const FEED_QUERY = gql`
  {
    feed {
      links {
        id
        createdAt
        url
        description
        postedBy {
          id
          name
        }
        votes {
          id
          user {
            id
          }
        }
      }
    }
  }
`

现在运行程序可以正确的展示更改后的页面了,还缺少一些变更请求。

增加变更

打开Link.js,添加以下变更定义在文件中

const VOTE_MUTATION = gql`
  mutation VoteMutation($linkId: ID!) {
    vote(linkId: $linkId) {
      id
      link {
        votes {
          id
          user {
            id
          }
        }
      }
      user {
        id
      }
    }
  }
`

然后用以下代码替换,类名为flex items-center的div

<div className="flex items-center">
  <span className="gray">{this.props.index + 1}.</span>
  {authToken && (
    <Mutation mutation={VOTE_MUTATION} variables={{ linkId: this.props.link.id }}>
      {voteMutation => (
        <div className="ml1 gray f11" onClick={voteMutation}>
          ▲
        </div>
      )}
    </Mutation>
  )}
</div>

接下来就是熟悉的添加Mutation组件调用voteMutation功能了,将依赖引入Link.js中

import { Mutation } from 'react-apollo'
import gql from 'graphql-tag'

现在可以运行项目,然后尝试点击点赞按钮,在刷新页面之后可以成功看到页面变更,实时的更新我们下面做改动。

更新缓存

在Apollo中我们可以手动控制缓存的内容,这使得我们在变更请求之后方便确认如何更新缓存。我们先在要做的就是确保UI在发送点赞变更后立即显示正确的点赞数。

打开Link.js,更新Mutation:

<Mutation
  mutation={VOTE_MUTATION}
  variables={{ linkId: this.props.link.id }}
  update={(store, { data: { vote } }) =>
    this.props.updateStoreAfterVote(store, vote, this.props.link.id)
  }
>
  {voteMutation => (
    <div className="ml1 gray f11" onClick={voteMutation}>
      ▲
    </div>
  )}
</Mutation>

在服务器返回响应后,将调用update函数,参数分别为store:当前缓存,data:组件的数据。
我们已经写好了update函数,实际的实现将在LinkList中完成,打开LinkList.js,在LinkList组件中加入:

_updateCacheAfterVote = (store, createVote, linkId) => {
  const data = store.readQuery({ query: FEED_QUERY })

  const votedLink = data.feed.links.find(link => link.id === linkId)
  votedLink.votes = createVote.link.votes

  store.writeQuery({ query: FEED_QUERY, data })
}

我们首先获取来当前的缓存数据,检索刚刚点赞的链接,更新该点赞数为服务器返回的数据,最后将data写入store中。接下来,我们需要将此功能传递给Link。
更新LinkList中使用Link组件部分:

<Link
  key={link.id}
  link={link}
  index={index}
  updateStoreAfterVote={this._updateCacheAfterVote}
/>

至此,我们完成了在点赞变更发送后,实时更新UI的功能。然后我们也同样加入在添加新链接请求之后的实时更新吧。

打开CreateLink.js,更新Mutation组件

<Mutation
  mutation={POST_MUTATION}
  variables={{ description, url }}
  onCompleted={() => this.props.history.push('/')}
  update={(store, { data: { post } }) => {
    const data = store.readQuery({ query: FEED_QUERY })
    data.feed.links.unshift(post)
    store.writeQuery({
      query: FEED_QUERY,
      data
    })
  }}
>
  {postMutation => <button onClick={postMutation}>Submit</button>}
</Mutation>

这个update函数和之前类似,我们在CreateLink引入依赖,以正常运行

import { FEED_QUERY } from './LinkList'

同时,我们需要将FEED_QUERY从LinkList中export出来

export const FEED_QUERY = ...

yeah~我们的应用现在可以在变更后实时更新UI了!

本节我们成功完成了更多的变更以及更新store的操作!马上开始下一步的旅程吧。

本章项目github 分支地址

https://github.com/zust-hh/simple-hackernews/tree/Frontend-topic6

 类似资料: