20200604のReactに関する記事は3件です。

Reactでaxiousを使って詳細ページを作る�

今回はaxiousを使ってタスク詳細ページを作る

APIを記述

src/service/v1ApRequest.js
import axios from 'axios'

// Taskの詳細ページのAPI
export const getTaskSubscriptionDetail = (id = ":id") => axios.get(`${API_ROOT}/task_subscriptions/${id}`)

:point_up_tone1:APIのリクエストがたくさんあるときは一つのファイルにまとめておくとわかりやすい

タスク詳細ページ作成

src/containers/TaskDetailPage.js
import { getTaskSubscriptionDetail } from '../services/v1ApiRequest'
import { SET_TASKSUBSCRIPTION } from '../constants/actionTypes'
import TaskSubscription from '../models/taskSubscription'

class TaskDetail extends Component {
  componentDidMount() {
    // this.props.match.params.idは
    // ..../path/to/:id の :idの部分をとってくれる
    // ..../path/to/:hoge_id だったら
    // this.props.match.params.hoge_id で取得できる
    // mapDispatchToPropsで定義したAPIを呼び出してStoreに保存するための関数を実行
    this.props.fetchTaskDetail(this.props.match.params.id);
  }

  render() {
    return (
          <div className={styles.taskName}>{this.props.taskSubscription.task.name}</div>
    )
  }
}
const mapStateToProps = ({ app, taskSubscription }) => ({
  isLoading: app.isLoading,
  taskSubscription: taskSubscription.selected // 4. APIのレスポンスがここに入る
})

//APIの結果をactionとしてdispatchする→コンポーネント側でやるならmapDispatchPropsを使う
const mapDispatchToProps = dispatch => ({
  fetchTaskDetail: (id) => {
    // 1. API呼び出して
    getTaskSubscriptionDetail(id)
      .then((res) => {
        // 2. レスポンスを受け取って
        console.log(res.data);
        // 3. dispatchにレスポンスを変換したもの(API)をpayloadにセットしたアクションをReducerへ運ばせる
        dispatch({
          type: SET_TASKSUBSCRIPTION,
      //payload: res.data だとスネークケースになるので、以下でキャメルケースにする
          payload: TaskSubscription.newFromApiResponse(res.data)
        })
      })
      .catch((error) => {
        // API読んでる最中にエラーが発生したらこっちが実行される
        console.log(error);
      });
  }
})
export default connect(
  mapStateToProps,
  mapDispatchToProps
)(withRouter(TaskDetail))


ルーティング

src/routes/index.js
 tasks: (id) => `/tasks${id ? `/${id}` : "" }`, 
// idがないときは一覧(/tasks)、idがあるときは詳細(/tasks/:is)のページへ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

microCMSのプレビュー機能をGatsby.jsで使う

Gatsby.jsは言わずと知れたReact製の静的サイトジェネレーターですが、ビルド時にページを生成するという性質ゆえに、microCMSのプレビュー機能を使うにはひと工夫必要だったので共有したいと思います。
この機能をご存知の方はコードの部分からご覧ください。

microCMSの記事プレビュー機能について

microCMSのプレビュー機能について少し説明しておくと、記事執筆画面で
image.png
公開ボタンの左にある画面プレビューを押した時に、執筆中の記事が実際にはどのように表示されるか確認できる機能です。仕組みとしては、

  • 「画面プレビュー」をクリック
  • 予め設定しておいたURLの変数部分(contentsIdとdraftKey)がクリックした記事のものに置き換えられたURLにアクセス image.png

という感じです。
詳しくはhttps://microcms.io/blog/draftkey_and_preview/ に掲載されています。

 何が問題でどうしたらいいのか?

この機能をGatsbyで使用する時、考えなければならないのは、Gatsby.jsの記事データは基本ビルド時に生成された物なので、
「画面プレビュー」クリック時に送られてきたcontentIddraftKeyを基に記事データをリアルタイムに取得して、表示する仕組みを作らないといけない。ということです。

 ということでコード

前置きはそのくらいにしてコードはこのようになりました。

pages/previewPage.jsx
import React, { useState, useEffect } from "react";
import { getSearchParams } from "gatsby-query-params";
//...その他コンポーネントなど

function previewPage() {
  //microCMS側に設定するurlはhttps://xxxxxx.com/previewPage/?contentId={CONTENT_ID}&draftKey={DRAFT_KEY}みたいな感じ
  const queryParams = getSearchParams();

  //queryParamsには
  //{contentId:"xxxx",
  //draftKey:"xxxx"}
  //というようなデータが入ります。

  const contentId = queryParams.contentId;
  const draftKey = queryParams.draftKey;

  const [postData, setPostData] = useState(null);//最初、postDataにはnullが入ります。

  useEffect(() => {
    if (!postData) {
      fetch(
        `https://xxxxxxxxx.microcms.io/api/v1/blogs/${contentId}?draftKey=${draftKey}`,
        {
          headers: {
            "X-API-KEY": "xxxxxxx-xxxx-xxxxx-xxxx-xxxxxxxx",
          },
        }
      )
        .then((response) => {
          if (response.ok) {
            return response.json();
          }
        })
        .then((json) => {
          postData = setPostData(json);//ここで、postDataに取得したコンテンツが格納されます。
        });
    } else {
      return function cleanup() {
        console.log("done");
      };
    }
  });
  return (
        <div
          dangerouslySetInnerHTML={{
            __html: postData ? postData.sentence : "",
          }}
        ></div>
  );
}

export default previewPage;

このようなページを作成することで、アクセスした時にurlクエリパラメータの情報を基にコンテンツをとってきてくれます。
以下、詳しく説明していきます。

API部分について

microCMSはPOSTとGETのAPI-KEYが違うのでGETのAPIが露出しても大して問題はないですが、なんとなく気持ちが悪いので、
X-API-KEY: "xxxxx-xxxx"としている部分とmicroCMSのurl部分は実際には環境変数に置き換えるのが望ましいです。環境変数の使い方に関してはこちらの記事の中でも説明しているので是非ご覧ください。

gatsby-query-paramsについて

2行目に登場するgatsby-query-paramsこちらです。
使用するにはターミナルでnpm i gatsby-query-paramsしてください。

コメントアウト部分にもありますが、getSearchParamsによって、urlクエリパラメータの情報をqueryParamsにオブジェクト形式で格納しています。

useState useEffectについて

これらはReactのhookで、詳しい説明はこちらにあります。
useEffectの中で、↑で取得したクエリパラメータを基にmicroCMSにデータを取得しにいき、取得できたら、それをpostDataに格納しています。

responce.okについて

これがないとmicroCMSのレスポンスが404の時(useStateがnullの間)返ってくる空文字列""を処理しようとしてしまい、jsonオブジェクトでないのでエラーが出てしまいます。

レンダリング部分について

return()内のpostData ? postData.sentence : ''としている部分で、postDataに↑でとってきた記事データが格納されたら、それが表示されるようになっています。postData.sentenceと書いていますが、sentenceの部分はmicroCMSで定義した表示したいデータのフィールドIDが入ります。レンダリング部分は他の記事ページのレイアウトに合わせて、煮るなり焼くなりしてください!

まとめ

サイトを見にきた人が、/今回作ったページと打つとアクセスできてしまいますが、contentsIdとdraftKeyがないので何も表示されないですし、一応は問題ないと思います。

色々と改善の余地はありそうですが、とりあえずこれで投稿前の記事をプレビューで確認できるので、是非活用してみてください!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactでstateの変更が反映されない

起こったこと

Reactで親コンポーネントでー行った、stateの変更が子コンポーネントで反映されません。
具体的には親コンポーネントは子コンポーネント1と子コンポーネント2を持っています。
子コンポーネント1で親のstateを変更し、その結果を子コンポーネント2で表示させています。

解決策

公式ドキュメントにちゃんと書いてありました。

補足

props を state にコピーしないでください。これはよくある間違いです。

constructor(props) {
super(props);
// してはいけません
this.state = { color: props.color };
}
この問題はそれが不要(代わりに this.props.color を直接使用することができるため)であり、バグの作成につながる(color プロパティの更新は state に反映されないため)ことです。

意図的にプロパティの更新を無視したい場合にのみ、このパターンを使用してください。その場合は、プロパティの名前を initialColor または defaultColor に変更してください。その後、必要に応じてキーを変更することで、コンポーネントにその内部の state を「リセット」させることができます。

もしあなたが props に依存する何らかの state が必要だと思うなら、どうすればいいのか学ぶために私達の派生 state を避けることについてのブログ記事を読んでください。

propsの値をconstructorの中で初期値として与えると、子コンポーネントは親のstateの変更を感知できないようです。
多くのサイトでこのような書き方をしていたので、結構間違えている人も多いと思います。

結局、

constructor(props) {
  super(props);
  this.state = {postUser: props.postUser};
}

としているのをやめて、直接子コンポーネント内でprops.postUserを使えば解決です。
super(props);を消すと子コンポーネントでpropsが使えなくなるので、これは残しておきます。

教訓

結局、公式が一番強い。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む