チャベログ

easy-notion-blogのいいねボタンを改善する

画像が読み込まれない場合はページを更新してみてください。

このブログにはいいねボタンの機能がページ最下部にあります。

画像が読み込まれない場合はページを更新してみてください。

いいねボタンの仕組みや追加方法については、下記のページをご参照下さい。

このいいねボタンにはしばらく問題がありました。いいねボタンを押しても、ページをリロードしないといいね数が反映されないという問題です。

この問題をツイートしたところ、easy-notino-blog の開発者のおとよさんが下記の様なアドバイスをくれました。

上記のヒントを元にいいねボタンを押すといいね数が即座に反映するように修正できたので、今回はその内容についての記事となります。

実際の動作は以下の動画です。右側のページでいいねボタンを押すと、左側の Like というプロパティの数が一つ増えます。その後、何度もいいねボタンを押してもいいね数に変化はありません。

画像が読み込まれない場合はページを更新してみてください。

修正内容

まずは、src/pages/api/like.tsを修正します。

変更前

getPostBySlug(slug as string)
    .then(post => {
      if (!post) throw new Error(`post not found. slug: ${slug}`)
      return post
    })
    .then(post => incrementLikes(post))
    .then(() => {
      res.statusCode = 200
      res.end()
    })
    .catch(e => {
      console.log(e)
      res.statusCode = 500
      res.end()
    })
src/pages/api/like.ts

変更後

try {
    const post = await getPostBySlug(slug as string)
    if (!post) {
      throw new Error(`post not found. slug: ${slug}`)
    }

    await incrementLikes(post)

    res.statusCode = 200
    res.end()
  } catch (e) {
    console.log(e)
    res.statusCode = 500
    res.end()
  }
src/pages/api/like.ts

ポイントとしては、

  • getPostBySlug(slug as string) では更新前の Like数を含んだpostが返ってくる。
  • incrementLikes(post)では更新後のLike数を含んだpostが返ってくる。
  • awaitを使わないとPromise { <pending> }が返ってきてうまくいかない。

というところです。

incrementLikes(post)Like数を更新する関数なので、incrementLikes()postを渡すことでLike数を更新したpostが返ってきます。

続いて、src/components/like-button.tsxを修正します。

変更前

const LikeButton = (props: Props) => {
  const [active, setActive] = useState(false)

  const handleClick = () => {
    if (!active) {
      axios.put(`/api/like?slug=${props.slug}`, {})
      setActive(true)
    }
  }
src/components/like-button.tsx

変更後

const LikeButton = (props: Props) => {
  const [active, setActive] = useState(false)
  const [like, setLike] = useState(props.post)

  const handleClick = () => {
    if (!active) {
      axios.put(`/api/like?slug=${props.slug}`, {})
      setActive(true)
      setLike((like) => like + 1)
    }
  }
src/components/like-button.tsx

ポイントとしては、

  • propsに 更新後の Like数が入って渡ってくるので、 useState関数を使ってLike数をstateとして管理することで、Like数が変化したタイミングで再レンダリングする。

というところです。

ちなみにstateの宣言は、以下の様に行います。

const [ state変数, state変数を更新するための関数 ] = useState( state変数の初期値 )

stateの更新は、state変数を更新するための関数を使用することで行います。

state変数を更新するための関数には、更新前のstateの値が渡され、その結果を返します。

今回の場合は、以下の部分でLike数をstateとして管理しています。

//stetaの宣言
const [like, setLike] = useState(props.post)
・
・
・
//stateの更新
setLike((like) => like + 1)

これで、Like数が変化したタイミングで再レンダリングをすることができるようになりました。

ちなみに、この部分には別のuseState関数も使われています。

const [active, setActive] = useState(false)

const handleClick = () => {
    if (!active) {
      ・
			・
			・
      setActive(true)
      ・
			・
			・
    }
  }

上記のようにactivestateとして管理してif文で条件分岐することで、初回はactivefalseなのでLike数の更新を実行、2回目以降はactivetrueなのでLike数の更新を実行しないという処理が実現できています。

少しだけ問題点

いいねボタンを押していいね数が更新されても、すぐにリロードするといいね数が元に戻ってしまいます。

これは このブログが ISR を使っていることが原因です。ISR は一定期間が経つまでは古いキャッシュを返すので、リロードすると更新前のいいね数が反映されます。しかし、Notion 側のいいね数は更新されているので、一定期間たってリロードすると Notion のデータベースに入っているいいね数が表示されます(このブログの場合は、一定期間 = 60秒 に設定されています。)

いいね数を押した後にリロードするという動作はなかなかない動作だと思うので、今回は良しとします。

画像が読み込まれない場合はページを更新してみてください。

終わりに

今回の対応は、おとよさんのアドバイスのおかげで何とか実現することができました。

実現はできたのですが、実は後でhoromiさん( easy-notion-blog のコミュニティ運営)が同じようなことを実現していることに気づき、最終は horomi さんのコードをかなりマネさせてもらいました。

easy-notion-blog 関連のカスタマイズで困ったことがある場合は、おとよさんか horomi さんのリポジトリを参考に見てみるのがおすすめです。