1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

React + RailsのSPAでdestroyアクションを実装する

Posted at

これは何?

React+Railsで簡単なタスク管理アプリを作っています。
ラベルの隣にある「削除」ボタンを押すと、確認メッセージののち、ラベルが削除されます。

Image from Gyazo

実行環境は以下の通りです。

  • Rails 6.0.3
  • React 17.0.2
    また、今回のディレクトリ構成は以下の通りです。(関係のある箇所だけ表示)
.
├── controllers
│   └── api
│       └── labels_controller.rb
└── javascript
    └── pages
        └── Labels.jsx

Rails側

ある意味衝撃でした。詳細確認中ですが、controllerにもroutesにもdestroy関連アクションを何も記載しなくても動作しました。(axiosのためかしら。。。。)

SPA側

削除メソッド自体はとてもシンプルで、axios.delete(URL[, オプション])で渡せばOKでした。その他、実装においてスラスラといかなかったところは解説します。

import React, { useState } from 'react'
import axios from 'axios'

export const Labels = (props) => {
  const [labels, setLabels] = useState([])

  React.useEffect(async () => {
    const response = await axios.get('/api/labels');
    setLabels(response.data)
  }, [])

  const removeLabel = (labelId, e) => {
    e.preventDefault();
    if (window.confirm("ラベルを削除します。よろしいですか?")) {
      axios.delete("/labels/" + labelId, {
        headers: {  // ★1解説します
          'X-Requested-With': 'XMLHttpRequest',
          'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
        }
      })
      .then( res => {  // ★3解説します
        const targetIndex = labels.findIndex( label => {
          return label.id === res.data.id
        })
        const newLabels = labels.slice();
        newLabels.splice(targetIndex, 1);
        setLabels(newLabels)
      })
      .catch(data => {
        console.log(data);
      })
    }
  }
  return (
    <div>
      <h1>ラベル一覧</h1>
        // 略
      <h3>ラベル</h3>
      <ul>
        {labels.map(label => (
          <li key={label.id}>
            {label.name}
            <a href="" onClick={(e) => removeLabel(label.id, e)}>削除</a> // 2解説します
          </li>
        ))}
      </ul>
    </div>
  )
}

CSRFトークン

★1の部分です。RailsではGET以外のajaxリクエストにデフォルトで、ヘッダーにセキュリティトークンを付与します(詳細はRailsセキュリティガイドを参照)。

axios.delete("/labels/" + labelId, {
  headers: {
    'X-Requested-With': 'XMLHttpRequest',
    'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
     }
   })

AjaxでGET以外のリクエストを実施するときには、リクエストヘッダーのX-CSRF-Tokenの項に、トークンを付与する必要があります。

トークンは、<head>タグ内、<meta name="csrf-token" ...>contentに記載されています。これは別のRailsアプリの画像ですが、該当箇所にトークンがありますね。
Image from Gyazo

トークンの上の行にある、こちらの記載は詳しく調べきれていないのですが、

'X-Requested-With': 'XMLHttpRequest',

こちらの記事によると、

また、クロスドメインの場合、headerに'X-Requested-With' : 'XMLHttpRequest'を入れて送信しなければ、api側でAjaxであることが伝わらない...

引用:クロスドメインで'X-Requested-With'の設定
とのことだったので、RailsにAjaxのリクエストであることを伝えるコードなのだと今は理解しておきます。

onClickイベントの渡し方

★2の部分です。「削除」のリンクがある箇所です。

<a href="" onClick={(e) => removeLabel(label.id, e)}>削除</a>

大したことではないのですが、Reactチュートリアルにこのような記載もあるくらい、間違えやすい箇所でしたので、備忘も兼ね記載しておきます。

onClick={() => alert('click')} と記載したときに onClick プロパティに渡しているのは関数であることに注意してください。React はクリックされるまでこの関数を実行しません。() => を書くのを忘れて onClick={alert('click')} と書くのはよくある間違いであり、こうするとコンポーネントが再レンダーされるたびにアラートが表示されてしまいます。

JSの式を渡さずに、関数を渡すように心がけます。

findIndex とか splice とか

★3の部分です。ここも難しいことはしていないのですが、私がJSに詳しくないので、勉強も兼ねての記載です。

なお、ロジックそのものはこちらの記事のものを利用させていただきました。

.then( res => {
  const targetIndex = labels.findIndex( label => {
    return label.id === res.data.id
  })
  const newLabels = labels.slice();
  newLabels.splice(targetIndex, 1);
  setLabels(newLabels)
})
  • findIndex ... **配列.findIndex(条件)**の形で、条件を満たす最初の要素のインデックス番号を返します。条件を満たすものがない場合は-1を返します。

  • splice ... **配列.splice(スタート位置[, 削除する数])**の形で、スタート位置(インデックス番号)から指定の数の要素を取り除きます。

完成!

以上で、削除機能の実装は完了です。次回は、「1回1回フォームにリクエストを書くのは面倒なので、まとめて一括で設定する」に取り組んでみたいです。

色々やったけどうまくいかなかった。。。。
また、アップデートアクションを作ったら続きを記載します。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?