20210106のReactに関する記事は7件です。

React Redux Hooks公式ドキュメント翻訳(概要編)

Reactアプリケーションの状態管理のためのOSSライブラリである、React Reduxのバージョン7.1.0で追加された、Hooks APIの公式ドキュメントを翻訳していきます。

2021/1/6公開。原文リンクは以下。
・公式ドキュメント(React Redux Hooks):https://react-redux.js.org/api/hooks

今回の記事は概要編です。
スクリーンショット 2021-01-06 23.07.19.png

Hooks APIとは

Reactの新しいhooks APIはローカルなコンポーネントのstateをより簡単に扱うことができる関数コンポーネントです。

React Reduxは今回、高階コンポーネント(HOC)の一つである既存のconnect関数の代わりに、いくつかのhook APIを提供します。これらのAPIを使えば、connect関数であなたのコンポーネントをラップすることをせずとも、ReduxのStoreに登録し、Actionを送る(dispatchする)ことが可能になります。

Hooks APIはReact Reduxのバージョン7.1.0で追加されました。

React ReduxアプリでHooks APIを使う

connect関数と同様に、Hooks APIを使う前の準備として、コンポーネントの階層全体でReduxのStoreを使えるように、以下のようにProviderを設定する必要があります。

const store = createStore(rootReducer)

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

この準備が整えばこれから紹介する、React Redux hooks APIの全てをあなたの関数コンポーネントの中で使用することができます。

次回はHooks APIの一つ、useSelector()について説明します。

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

個人でWebサービス(パズルアプリ)をリリースしたときの話

作ったサービス

概要

オートマトンのパズルアプリです。
お題のオートマトンをユーザが作成していくサービスです。

ここでは、動機や使用技術、ユーザの反応を中心に記載していきます。
※詳しいサービスの内容は、WebサービスのURLから見ることができます。

プレイイメージ

play.gif

WebサービスのURL

https://www.loving-automaton.com#/

ソースコード

https://github.com/threeislands/automaton-app

動機

以前、オートマトンに関する本を読んだときに、正規言語とその状態遷移図が記載されていました。

そのときに「正規言語から状態遷移図を作成する過程は、非常に面白いのではないか!?」と思いました。

他の理由

  • オートマトンの描画を実装できるか検証したい
  • オートマトンの面白さを他の人にも伝えたい!!

そこで、実際につくってみることにしました。

機能概要

  • オートマトン描画機能
  • オートマトン判定機能
  • ログイン機能
  • オートマトンの保存/取得機能(ログイン済みのユーザのみ)

技術スタック

概要

今回は、フロントエンドとバックエンドを分離した SPA + BFF の構成にしました。

理由

  • これまでMVCフレームワークを使ったWeb開発が多かったのですが、その際にテンプレートエンジン(Thymeleaf)とJavaScript(jQuery)のそれぞれの変数が混在していて、可読性があまり高くないと感じたため
  • SPAを採用した場合の認証やログインの実装方法に興味があったため

フロントエンド

React, (D3)

SPAフレームワークには、Reactを採用しました。
また、一部にD3を利用しました。(ドラッグ&ドロップの処理)

バックエンド

Flask, SQLAlchemy

バックエンドのAPIサーバのフレームワークに、Flaskを利用しました。
また、ORMには、SQLAlchemyを利用しました。
marshmallowというライブラリを利用することで、簡単にモデル⇔JSONを相互変換できるのが便利でした。

インフラ

GitHub Pages

フロントエンド(React)はGitHub Pages上で、運用しています。
GitHub PagesはPublicリポジトリであれば、無料で利用できる点が魅力的でした。
また、gh-pagesというライブラリ利用することで、簡単にデプロイできるのも良いと思いました。

AWS (CloudFront, Elastic Beanstalk(EC2))

バックエンド(Flask)はElastic Beanstalk(EC2)上で、運用しています。
CloudFrontにSSL証明書を設定して、HTTPSにしています。

クライアント(ブラウザ) - GitHub Pages 間にCloudFrontを配置することで、フロントエンドとバックエンドのoriginを同じにしています。
それによって、CookieにSameSite属性(=Lax)を指定して、CSRFの対策をおこなえるようにしています。(Cookieベースの認証を利用している)

インフラ構成

cluod_infra.jpg

リクエストパスが /api から始まる場合、CloudFrontでEC2にリバースプロキシするようにしています。
それ以外のリクエストパスの場合、Github Pagesにリバースプロキシするようにしています。

認証

概要

今回は、ログイン機能にGoogleのOpenID ConnectとTwitterのOAuth1.0を利用しました。

Google / OpenID Connect の場合

  1. 外部サービスのログイン後に、APIサーバにリダイレクトする
  2. リダイレクト時のパスに含まれるコード値から、id_tokenを取得する外部APIを実行する
  3. 3. 取得したid_tokenのsubがユーザテーブルに登録されていれば(未登録の場合、新規登録)、SessionにユーザIDを設定して、認証済みのユーザとする

Twitter / OAuth1.0 の場合

  1. 外部サービスのログイン後に、APIサーバにリダイレクトする
  2. リダイレクト時のパスに含まれるoauth_token, oauth_verifierから、アクセストークンを取得する外部APIを実行する
  3. 取得したトークンのuser_idがユーザテーブルに登録されていれば(未登録の場合、新規登録)、SessionにユーザIDを設定して、認証済みのユーザとする

フロントエンド

React

初回ロード時に、ユーザ情報取得のAPIを実行します。
認証済みの場合のみ、ユーザ情報を取得できます。
取得したユーザ情報を、UserContext に設定して他のコンポーネントから参照できるようにしています。

App.js
function App() {

  const [user, setUser] = useState(null);

  useEffect(() => {
    const api = async () => {
      const user = await UserService.getUser();
      // 未ログインの場合、空のobjectが返却されるのでnullを設定
      if (Object.keys(user).length === 0) {
        setUser(null);
      } else {
        setUser(user);
      }
    }
    api();
  }, []);

  return (
    <div className="App">
      <CssBaseline/>
      <Suspense fallback="loading">
        <UserContext.Provider value={user}>
          {/* Other Components... */}
        </UserContext.Provider>
      </Suspense>
    </div>
  );
)

UserContextの参照例(該当箇所のみ抜粋)
ログイン状態に応じて、プロフィール欄とログインボタンの表示を切り替える

Header.js
function Header(props) {

  const user = React.useContext(UserContext);
  const profileArea = user ? UserProfile : LoginButton;

  return (
     <Container>
      <Grid>
        {profileArea()}
        {/* Other Components */}
      </Grid>
    </Container>
}

API (axios)

APIを実行するライブラリとして、axiosを利用しています。
リクエストの際にCookieも送付するように、withCredentialsをの設定をします。

base-service.js
export default axiosBase.create({
  baseURL: Constant.API_ENDPOINT,
  headers: {
    'Content-Type': 'application/json',
  },
  withCredentials: true,
  responseType: 'json'
});

バックエンド

認証状態をチェックするデコレータ

今回は認証用のライブラリを利用せずに自前で実装しました。
Sessionに user_id が設定されている場合、認証済みとしています。

authentication_required.py
from functools import wraps
from flask import session, jsonify

def authentication_required(f):

    @wraps(f)
    def decorated_function(*args, **kwargs):
        if session.get('user_id') is None:
            return jsonify({'message': 'Unauthorized'}), 401

        return f(*args, **kwargs)

    return decorated_function

上記デコレータの利用例

作成したオートマトンを保存できるAPI
認証済みのユーザのみ、このAPIを呼び出すことができる

user_api.py
@bp.route('/user/save_automaton/<int:question_id>', methods=['POST'])
@authentication_required
def save_automaton(question_id):
    user_id = session.get('user_id')

    req_body = request.json
    schema = AutomatonSchema()
    automaton_data = schema.load(req_body)

    created_automaton = CreatedAutomaton(
        user_id=user_id, question_id=question_id,
        automaton_data=schema.dump(automaton_data))

    AutomatonService.create(created_automaton)

    return make_response(jsonify({'message': 'success'}))

印象に残っていること

タイトル/デザインについて

当初は、タイトルを「オートマトンの部屋」にして、Mine◯raftのような配色にすることを想定していました。

開発中に、「恋する寄◯虫」という漫画から着想を得て、「恋するオートマトン」が思いつき、語呂がけっこう良かったので採用しました。

デザインもタイトルに合わせて、ピンクと水色を基調としたPOPな配色にしました。

エフェクトについて

当初は、回答時のアニメーションにエフェクトをつけることは予定していませんでした。

しかし、アニメーション実装後に(エフェクトなしで)プレイしてみたところ、爽快感や気持ちよさをあまり感じませんでした。

そこで、正解時に花火のようなエフェクトを設定しました。
多少なりとも、爽快感を感じるようになったと思います。

animation2.gif

エフェクトのアニメーションはSVGのanimate, animateMotion, animateTransformを利用して実装しました。
CSSの Key frame と異なり、動的にアニメーションの時間や変化量、角度を設定できます。

実際のソースは以下で確認できます。
https://github.com/threeislands/automaton-app/blob/master/frontend/src/components/ClearEffect.js

リリース後の反応

リリースして、友人や知り合いなどだいたい20人ぐらいの人にプレイしてもらうことができました。

ポジティブなフィードバック

  • 面白い
  • 楽しかった

エンジニアやゲームが好きな人、数学好きな人からは好意的なフィードバックをもらうことが多く、とても嬉しかったです。
また、レベル7や8(結構むずかしい問題)でもあっさりとクリアしてしまう人がいたことには、とても驚かされました。

どちらかというとネガティブなフィードバック

  • 難しすぎる
  • 意味分かんない!
  • 説明は理解できたけど、どうやっていいかわからない
  • 出会い系サイトっぽい

まず、プレイするにあたって理解しておくルールが多いため、ユーザの認知的な負荷が大きくなってしまいました。
改善案として、インタラクティブなオンボーディングを導入することを検討したいです。(ユーザの操作ごとに説明文が更新される等)

出会い系サイトっぽいという意見はちょっと意外でした。笑
素敵なオートマトンに出会えるので、ある意味出会い系サイトかもしれません!?

感想

今回、初めて個人でサービスを開発してリリースまでおこなうことができました。

自分の開発したサービスをユーザが利用してくれることはとても嬉しく、フィードバックからも多くの洞察を得ることができました。

今回作成したサービスは、自分の作りたいサービスでしたが、次回はユーザの潜在的なニーズに合致するサービスを作りたいと思います。

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

個人開発でWebサービス(パズルアプリ)をリリースしたときの話

作ったサービス

概要

オートマトンのパズルアプリです。
お題のオートマトンをユーザが作成していくサービスです。

ここでは、動機や使用技術、ユーザの反応を中心に記載していきます。
※詳しいサービスの内容は、WebサービスのURLから見ることができます。

プレイイメージ

play.gif

WebサービスのURL

https://www.loving-automaton.com#/

ソースコード

https://github.com/threeislands/automaton-app

動機

以前、オートマトンに関する本を読んだときに、正規言語とその状態遷移図が記載されていました。

そのときに「正規言語から状態遷移図を作成する過程は、非常に面白いのではないか!?」と思いました。

他の理由

  • オートマトンの描画を実装できるか検証したい
  • オートマトンの面白さを他の人にも伝えたい!!

そこで、実際につくってみることにしました。

機能概要

  • オートマトン描画機能
  • オートマトン判定機能
  • ログイン機能
  • オートマトンの保存/取得機能(ログイン済みのユーザのみ)

技術スタック

概要

今回は、フロントエンドとバックエンドを分離した SPA + BFF の構成にしました。

理由

  • これまでMVCフレームワークを使ったWeb開発が多かったのですが、その際にテンプレートエンジン(Thymeleaf)とJavaScript(jQuery)のそれぞれの変数が混在していて、可読性があまり高くないと感じたため
  • SPAを採用した場合の認証やログインの実装方法に興味があったため

フロントエンド

React, (D3)

SPAフレームワークには、Reactを採用しました。
また、一部にD3を利用しました。(ドラッグ&ドロップの処理)

バックエンド

Flask, SQLAlchemy

バックエンドのAPIサーバのフレームワークに、Flaskを利用しました。
また、ORMには、SQLAlchemyを利用しました。
marshmallowというライブラリを利用することで、簡単にモデル⇔JSONを相互変換できるのが便利でした。

インフラ

GitHub Pages

フロントエンド(React)はGitHub Pages上で、運用しています。
GitHub PagesはPublicリポジトリであれば、無料で利用できる点が魅力的でした。
また、gh-pagesというライブラリ利用することで、簡単にデプロイできるのも良いと思いました。

AWS (CloudFront, Elastic Beanstalk(EC2))

バックエンド(Flask)はElastic Beanstalk(EC2)上で、運用しています。
CloudFrontにSSL証明書を設定して、HTTPSにしています。

クライアント(ブラウザ) - GitHub Pages 間にCloudFrontを配置することで、フロントエンドとバックエンドのoriginを同じにしています。
それによって、CookieにSameSite属性(=Lax)を指定して、CSRFの対策をおこなえるようにしています。(Cookieベースの認証を利用している)

インフラ構成

cluod_infra.jpg

リクエストパスが /api から始まる場合、CloudFrontでEC2にリバースプロキシするようにしています。
それ以外のリクエストパスの場合、Github Pagesにリバースプロキシするようにしています。

認証

概要

今回は、ログイン機能にGoogleのOpenID ConnectとTwitterのOAuth1.0を利用しました。

Google / OpenID Connect の場合

  1. 外部サービスのログイン後に、APIサーバにリダイレクトする
  2. リダイレクト時のパスに含まれるコード値から、id_tokenを取得する外部APIを実行する
  3. 3. 取得したid_tokenのsubがユーザテーブルに登録されていれば(未登録の場合、新規登録)、SessionにユーザIDを設定して、認証済みのユーザとする

Twitter / OAuth1.0 の場合

  1. 外部サービスのログイン後に、APIサーバにリダイレクトする
  2. リダイレクト時のパスに含まれるoauth_token, oauth_verifierから、アクセストークンを取得する外部APIを実行する
  3. 取得したトークンのuser_idがユーザテーブルに登録されていれば(未登録の場合、新規登録)、SessionにユーザIDを設定して、認証済みのユーザとする

フロントエンド

React

初回ロード時に、ユーザ情報取得のAPIを実行します。
認証済みの場合のみ、ユーザ情報を取得できます。
取得したユーザ情報を、UserContext に設定して他のコンポーネントから参照できるようにしています。

App.js
function App() {

  const [user, setUser] = useState(null);

  useEffect(() => {
    const api = async () => {
      const user = await UserService.getUser();
      // 未ログインの場合、空のobjectが返却されるのでnullを設定
      if (Object.keys(user).length === 0) {
        setUser(null);
      } else {
        setUser(user);
      }
    }
    api();
  }, []);

  return (
    <div className="App">
      <CssBaseline/>
      <Suspense fallback="loading">
        <UserContext.Provider value={user}>
          {/* Other Components... */}
        </UserContext.Provider>
      </Suspense>
    </div>
  );
)

UserContextの参照例(該当箇所のみ抜粋)
ログイン状態に応じて、プロフィール欄とログインボタンの表示を切り替える

Header.js
function Header(props) {

  const user = React.useContext(UserContext);
  const profileArea = user ? UserProfile : LoginButton;

  return (
     <Container>
      <Grid>
        {profileArea()}
        {/* Other Components */}
      </Grid>
    </Container>
}

API (axios)

APIを実行するライブラリとして、axiosを利用しています。
リクエストの際にCookieも送付するように、withCredentialsをの設定をします。

base-service.js
export default axiosBase.create({
  baseURL: Constant.API_ENDPOINT,
  headers: {
    'Content-Type': 'application/json',
  },
  withCredentials: true,
  responseType: 'json'
});

バックエンド

認証状態をチェックするデコレータ

今回は認証用のライブラリを利用せずに自前で実装しました。
Sessionに user_id が設定されている場合、認証済みとしています。

authentication_required.py
from functools import wraps
from flask import session, jsonify

def authentication_required(f):

    @wraps(f)
    def decorated_function(*args, **kwargs):
        if session.get('user_id') is None:
            return jsonify({'message': 'Unauthorized'}), 401

        return f(*args, **kwargs)

    return decorated_function

上記デコレータの利用例

作成したオートマトンを保存できるAPI
認証済みのユーザのみ、このAPIを呼び出すことができる

user_api.py
@bp.route('/user/save_automaton/<int:question_id>', methods=['POST'])
@authentication_required
def save_automaton(question_id):
    user_id = session.get('user_id')

    req_body = request.json
    schema = AutomatonSchema()
    automaton_data = schema.load(req_body)

    created_automaton = CreatedAutomaton(
        user_id=user_id, question_id=question_id,
        automaton_data=schema.dump(automaton_data))

    AutomatonService.create(created_automaton)

    return make_response(jsonify({'message': 'success'}))

印象に残っていること

タイトル/デザインについて

当初は、タイトルを「オートマトンの部屋」にして、Mine◯raftのような配色にすることを想定していました。

開発中に、「恋する寄◯虫」という漫画から着想を得て、「恋するオートマトン」が思いつき、語呂がけっこう良かったので採用しました。

デザインもタイトルに合わせて、ピンクと水色を基調としたPOPな配色にしました。

エフェクトについて

当初は、回答時のアニメーションにエフェクトをつけることは予定していませんでした。

しかし、アニメーション実装後に(エフェクトなしで)プレイしてみたところ、爽快感や気持ちよさをあまり感じませんでした。

そこで、正解時に花火のようなエフェクトを設定しました。
多少なりとも、爽快感を感じるようになったと思います。

animation2.gif

エフェクトのアニメーションはSVGのanimate, animateMotion, animateTransformを利用して実装しました。
CSSの Key frame と異なり、動的にアニメーションの時間や変化量、角度を設定できます。

実際のソースは以下で確認できます。
https://github.com/threeislands/automaton-app/blob/master/frontend/src/components/ClearEffect.js

リリース後の反応

リリースして、友人や知り合いなどだいたい20人ぐらいの人にプレイしてもらうことができました。

ポジティブなフィードバック

  • 面白い
  • 楽しかった

エンジニアやゲームが好きな人、数学好きな人からは好意的なフィードバックをもらうことが多く、とても嬉しかったです。
また、レベル7や8(結構むずかしい問題)でもあっさりとクリアしてしまう人がいたことには、とても驚かされました。

どちらかというとネガティブなフィードバック

  • 難しすぎる
  • 意味分かんない!
  • 説明は理解できたけど、どうやっていいかわからない
  • 出会い系サイトっぽい

まず、プレイするにあたって理解しておくルールが多いため、ユーザの認知的な負荷が大きくなってしまいました。
改善案として、インタラクティブなオンボーディングを導入することを検討したいです。(ユーザの操作ごとに説明文が更新される等)

出会い系サイトっぽいという意見はちょっと意外でした。笑
素敵なオートマトンに出会えるので、ある意味出会い系サイトかもしれません!?

感想

今回、初めて個人でサービスを開発してリリースまでおこなうことができました。

自分の開発したサービスをユーザが利用してくれることはとても嬉しく、フィードバックからも多くの洞察を得ることができました。

今回作成したサービスは、自分の作りたいサービスでしたが、次回はユーザの潜在的なニーズに合致するサービスを作りたいと思います。

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

[React]refのchildrenから特定のclassNameを持つElementを取得する

ライブラリのDOM触るときとかに使うのでメモ

import React, { useRef } from 'react'
import styled from 'styled-components'

export default function Foo() {
  const ref = useRef<HTMLDivElement>(null)

  React.useEffect(() => {
      const element = Array.from(ref.current?.children || []).find(
        (item) => item.className === 'some-class-name'
      )
      ...
  }, [ref])

  return (
    <>
      <SomeElement ref={ref} />
    </>
  )

  const SomeElement = styled.div``
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Blitz.jsの議論から学ぶ,formik vs react-final-form vs react-hook-form

What should the default form library be in Blitz apps?

BlitzがどのFormライブラリをデフォルトにするかの議論が参考になったので要約しました。

の3択で,結論としては,React Final Formを推奨とする形に落ち着きました。

最初のコメント

Blitz.jsの作者であるBrandon(@flybayer)さんがコメントしています。要約するとこんな感じ。

  • Formik
    以前使ってたけど,パフォーマンスめっちゃ悪いって気づいたから,Formikは無しで。

  • React-final-form
    最近ずっと使ってるけどいい感じ。

  • React-hook-form
    ほとんど使ったことない。

React-final-formをReact-hook-formに書き換えてみたが,コードの複雑さとしてはそこまで差異はない。
https://github.com/blitz-js/blitz/pull/767/files

議論

全体的にFormikは無いよねという感じでした。

気になった意見をpick upします。

react-hook-formのデメリット

対象のコメント→ https://github.com/blitz-js/blitz/discussions/768#discussioncomment-39148

別のFormライブラリとの互換性の低さ

MaterialUIなどのサードパーティー製のコンポーネントと一緒に使う場合は
<Controller>コンポーネントでwrapしなくてはいけない。

      <Controller
        name="iceCreamType"
        as={Select}
        options={[
          { value: "chocolate", label: "Chocolate" },
          { value: "strawberry", label: "Strawberry" },
          { value: "vanilla", label: "Vanilla" }
        ]}
        control={control}
        rules={{ required: true }}
      />

<Controller>を使わないパターンと使うパターンの2つの異なるAPIを知っておかなくてはいけない。

一方,react-final-formはuseField()というAPIだけで済むので,他のライブラリとの互換性が高い。

refの渡し方が不自然

focusしたときに何かを発動したいときなどはrefを自分で渡すことになるが,そのときのrefの渡し方が面倒くさい。

  const { register, handleSubmit } = useForm();
  const firstNameRef = useRef();
  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="firstName" ref={(e) => {
        register(e)              // hook-form register
        firstNameRef.current = e // manually assign your ref
      }} />

ref={ref}で渡したいのに…。

react-final-formのデメリット

render propsが初心者には難しいのではないか?というコメントがいくつかあった。

  • 基本的にrender propsを使う
    • react-final-form
    • formik
  • 基本的にrender propsを使わない
    • react-hook-form

それに対し,Brandonさんは以下のように反論しています。

there is only one top level render prop with final-form which also provides the form context.

トップ層でしか使用していないし,

So either way, you'll likely have a render prop. I definitely agree the primary API shouldn't be a render prop, but looks like it's impossible to 100% avoid render props.

react-hook-formでも<Controller/>を使ったらrender propsを使うことになるし,render propsから完全に逃れることはできないのではないか?とのことです。

まとめ

今回はReact Final Formを推奨とする結論になりましたが,「とにかく速く開発したいからみんなが使えて学習コストの低いreact hook formで」など,プロジェクトによって吟味する必要がありそうです。

「react-hook-formは広く使われているからみんな慣れてるだろうし良い」というような意見に対して「学習コストが低いものを選定基準にするのではなく,既存のプロジェクトをBlitzに置き換えることになったときになるべく痛みが少ないものを使いたい。」と返していて,ライブラリ選定の参考になる見方だと思いました。

追記

React Hook Formの作者である@bluebill1049さんがコメントしてくださりました。(詳しくは本人のコメントを参照してください!)

2020年12月ごろにリリースされたv6.13.0useControllerというhooksが追加されました。それを使えば<Controller>で囲ってRender propを使うという必要がなくなります。Material UIのコンポーネントなどを使う場合は<Controller>を使うしかありませんが,自作のコンポーネントを使う場合はuseControllerが使えます!

また,refについてもV7ではフォーカス管理を改善していく予定とのことです。

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

ReactでCan't perform a React state update on an unmounted component.の原因と解決

プログラミング開始7ヶ月目の者です。

ReactのクラスコンポーネントのcomponentDidMountについてです。

メソッド内でsetTimeoutの記述をしました。

  // 表示される毎に実行
  componentDidMount() {
    setTimeout(() => {
      this.setState({ isVisible: !this.state.isVisible });
    }, 1000);
  }

それでsetTimeoutで値が切り替わる前に別のLink(Router)に飛ぶとエラーが出ました。

エラー

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.

原因

componentDidMountしたのにunmountedされたまま、別のLinkに移った為です。
「マウントされないcomponentでstateを更新しようとしています。componentWillUnmountでキャンセルしてね。」

解決

componentWillUnmount用に変数とメソッドを追加し代入する。
これで別のページに移ったときにcomponentWillUnmountでキャンセルされます。

  // unmount用の変数
  handle;

  // 変数に代入
  componentDidMount() {
    this.handle = setTimeout(() => {
      this.setState({ isVisible: !this.state.isVisible });
    }, 1000);
  }
  // unmount用メソッドを追加 引数に変数を入れる。
  componentWillUnmount() {
   clearTimeout(this.handle);
  }

修正サンプル。
const props = {
  visible: {
    opacity: 1,
  },
  hidden: {
    opacity: 0,
  },
}
const Box = posed.div(props)

export default class Can extends React.Component {

  handle;

  state = {
    isVisible: false,
  };
  // mount
  componentDidMount() {
    this.handle = setTimeout(() => {
      this.setState({ isVisible: !this.state.isVisible });
    }, 1000);
  }
  // unmount
  componentWillUnmount() {
   clearTimeout(this.handle);
  }
  render() {
    return (
      <Box pose={this.state.isVisible ? 'visible' : 'hidden'}>出現</Box>
    );
  }
}

参考

エラーで悩んでいた時に見つけた 記事 です。

以上です!

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

Reactを書くときのコマンドが覚えられない話

Reactを書くときのコマンド,多くないですか?

前提

OS:MacOS Catalina (sedコマンドを使うので,Mac以外だと動かんかも)
ビルドツール:Parcel (npm install -g parcel)
Shell: Fish
必要なコマンド:gron (brew install gron)

経緯

最近Reactの勉強を始めたのですが,プロジェクトごとに使うコマンドが多くて覚えられません...
最初はReactとBabelくらいだったのでなんとかなっていましたが,プラグインなどが入ってきてどれがどれだかわからなくなってしまいました.
そこで,「プラグインとかだいたい使うの一緒だしシェルスクリプトにすればいいのでは!?!?」と思ったのでシェルスクリプトにしました.

TL;DR

react-init.fish
function react-init
  npm init
  npm install --save react react-dom
  npm install --save-dev babel-preset-react-app
  npm install --save-dev react-hot-loader

  mkdir -p src/js
  touch ./src/index.html

  echo '{
  "presets": ["react-app"],
  "plugins": ["react-hot-loader/babel"]
}'>.babelrc

  cat package.json|gron|sed 's/test/start/'|sed -E 's/"echo.*/"parcel .\/src\/index.html";/'|gron -u>package.json.bak
  rm package.json
  mv package.json.bak package.json
end

コード解説

Reactのプロジェクトを立ち上げるのに使いたいコマンドは

npm init
npm install --save react react-dom
npm install --save-dev babel-preset-react-app
npm install --save-dev react-hot-loader

の4つなので,これをシェルスクリプトに書きます.

次に,JavaScriptを入れておくディレクトリとindex.htmlを作成します.

mkdir -p src/js
touch src/index.html

次に,.babelrcを作成します.
react-hot-loaderを使わない場合は,pluginsは削除してください.

echo '{
"presets": ["react-app"],
"plugins": ["react-hot-loader/babel"]
}'>.babelrc

npm initで作成されたpackage.jsonにparcelのコマンドを追加していきます.
もともとnpm run test用のコマンドが書いてあるので,それを置換していきます.

余談ですが,gronコマンドはjsonをgrepしやすい形にしてくれるコマンドです.
初めて使いましたがかなり便利でした.

cat package.json|gron|sed 's/test/start/'|sed -E 's/"echo.*/"parcel .\/src\/index.html";/'|gron -u>package.json.bak
rm package.json
mv package.json.bak package.json

おわりに

調べてもこんなことしてる記事が見つからなかったので,絶対間違ってるんだろうなという感じです.
実際に開発するときにはpluginとかが変わってくるからやらないのかな?などと思いました.

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