20210729のReactに関する記事は12件です。

Formikの基本的な使い方と、MaterialUIでの使用例

以前の案件で使ったFormikのメモです。 公式ドキュメントがやはり一番詳しいですが、 初めて使う人は簡単な概要があったほういいかもしれないので、記事にしておきます。 Formikとは? FormikはReactのフォームの扱いをシンプルにしてくれるライブラリです。 Formik公式ドキュメント おそらく世界で一番使われているReactのフォームライブラリじゃないかなと思います。 npm trendsで他ライブラリと比較してみました。 react-hook-formも伸びてきていますが、他ライブラリとかなり差が開いていますね。 Formikは以下3点を受け持ってくれます。 formの状態管理 バリデーション フォームのsubmit処理 フォームの状態管理にはRedux-Formというライブラリもありますが、 Formikの方がシンプルっぽいです。 Redux使うと、複雑になりやすいので、 Formikの方がいいケースの方が多いんじゃないかなと思います。 バリデーションについては、Yupというライブラリとセットで使うことが多いです。公式でもYupいいよね的なこと言ってますね。 Formikの使い方概要 とりあえずインストールしましょう。 npm install formik --save // または yarn add formik 使い方は大きく分けて3種類あります。 useFormikを使って書くやり方 withForimikを使う書き方 JSXのreturn部分で<Formik>コンポーネントを使って書いていくやり方 Formikコンポーネントを使って書いた方が簡潔にかけます。 一方、materialUIなどのコンポーネントで使う場合にはuseFormikを使うというやり方があります。 この記事ではuseFormikを使った記述内容中心にして、 別記事でFormikコンポーネントを使った方法を書いて行こうかなと思います。 ちなみにFormikコンポーネントはuseFormikを内部的に使っています。 useFormikの使い方ざっくり 大体以下の感じになるかと思います。 useFormikを使い、引数や型でフォームの情報を設定。 フォームで何を扱うかを型で設定 初期値の設定 バリデーションの設定 formのonSubmit時の処理の設定 formタグのonSubmitプロパティにformik.handleSubmitを設定 各フォーム要素にformikのバリューやイベントを設定 コードで書いてみると以下のような感じのサンプルに鳴ります。 import { useFormik } from 'formik'; import * as Yup from 'yup'; const SampleComponent = () => { const formik = useFormik<Type>({ // フォームに初期値を設定 initialValues: { email: '', password: '', }, // バリデーション validationSchema: validationSchema, // submit時のファンクション onSubmit: values => { // valuesはformの現在の値を返す。 alert(JSON.stringify(values, null, 2)) }, }); return ( <form onSubmit={formik.handleSubmit}> <label htmlFor="email">Email Address</label> <input // idとnameをinitialValuesで設定したプロパティと合わせて設定 id="email" name="email" type="email" onChange={formik.handleChange} onBlur={formik?.handleBlur} value={formik.values.email} > <label htmlFor="password">Password</label> {formik.errors.email ? <div>{formik.errors.email}</div> : null} <input id="password" name="password" type="password" onChange={formik.handleChange} onBlur={formik?.handleBlur} value={formik.values.password} > {formik.errors.password ? <div>{formik.errors.password}</div> : null} <button type="submit">Submit</button> </form> ) } const SignupSchema = Yup.object().shape({ email: Yup.string().email() .required('Required'), password: Yup.string() .min(2, 'Too Short!') .max(50, 'Too Long!') .required('Required'), email: Yup.string().email('Invalid email').required('Required'), }); inputの設定プロパティが少し多い感じがしますね。 タグを使うやり方であれば、もっとシンプルに書けます。 よく使いそうなプロパティ 他にも色々とありますが、以下の3つはよく使いました。 // 特定のフォーム要素に値を設定。 formik.setFieldValue('email', 'aaa@example.com') // 複数のフォーム要素に値を設定。APIで取得した値を一括で設定したいときなどに利用 formik.setValues(values) // フォームの値をリセット formik.handleReset MaterialUIでの使い方 TextFieldやRadioButtonはアプリケーションの中でよくある要素なので、 コンポーネント分割されて使いまわされるケースが多いと思います。 そんな場合位はこんな感じにformikをpropsで渡してあげればいいです。 interface Props { name: string; formik?: ReturnType<typeof useFormik>; } const TextFieldParts: FC<Props> = ({name, formik}) => { return <TextField ~~ value={formik?.values[name]} onChange={formik?.handleChange} onBlur={formik?.handleBlur} error={Boolean(formik?.errors[name])} helperText={formik?.errors[name]} ~~ /> };
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TypeScript+Reactの構成でアプリを作成をしました【1】【天気アプリ】【随時更新】

はじめに 学習するに至った経緯 2020年より、未経験からエンジニアへの転職を目指し、某プログラミングスクールへ通う。 入学後、『Ruby』を未経験から学ぶ人が多いのと『Ruby』の求人が思っていた以上に少ないので、 卒業後、フロントエンドのエンジニアを目指す事に。 Javascriptの学習した事を言語化し、認識の深化による備忘録として記載。 場所を検索して現在の天気と24時間の天気予報を表示できるシンプルな天気アプリを作成します。 【作成にあたり学習した事】 アプリをブートストラップする create-react-app TypeScriptでReactを使用する Reactフック 非同期通信を開き天気API 基本的なスタイリング プログラムを書くポイント 小さく作って小さく動かす。 最初にタスクを明確にして、作成順を決める。 天気アプリのタスクの明確化と作成順 場所を検索して現在の天気と24時間の天気予報を表示できるシンプルな天気アプリを作成。 ① HTMLの場合と同じ方法でビューを構築 ➡︎ ユーザーが場所を検索できる入力を追加し、その場所をテーブルに追加する。 ② 場所の表の表示  ③ ④ プロジェクト用の新しいフォルダーを作成する npx create-react-app <プロジェクト用の新しいフォルダー名> --template typescript cd <プロジェクト用の新しいフォルダー> これにより、プロジェクトフォルダーが作成され、npm install(またはyarnインストールされていることが検出された場合に)実行される。 ファイル削除と書き換え 1.App.css及び内容の削除index.css 2.logo.svgからのインポートを削除しますApp.tsx 3.削除App.test.tsxしてlogo.svg 4.アプリ関数App.tsx(コンポーネント)の定義を次のように書き換える。 App.tsx function App() { return ( <div> <h1>Weather App</h1> </div> ); } 場所の表を表示する ① HTMLの場合と同じ方法でビューを構築 仕様:ユーザーが場所を検索できる入力を追加し、その場所をテーブルに追加する。⇨ 場所は文字列になり、テーブル内のすべての場所のリストは文字列配列になる。 App.tsx function App() { return ( <div> <h1>Weather App</h1> <div> <label> Add Location <input type="text" value="Paris"/> </label> <button>Search</button> </div> <div> <h2>Locations</h2> <table> <thead> <tr> <th>Name</th> </tr> </thead> <tbody> <tr><td>Belfast</td></tr> <tr><td>New York</td></tr> </tbody> </table> </div> </div> ); } 参考サイト Reactチュートリアル—最初から天気アプリを作成する—パート1 Prettierの導入方法 フロントエンド開発で必須のコード整形ツール VSCode + ESLint + Prettier + React + TypeScript (自分用メモ Fall, 2020) .prettierrc VS Codeのsettings.jsonの開き方 TypeScript+React用にprettierを導入する TypeScript+React用にESLintを導入する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【画像手順解説】Auth0 Rails(api) + React SPAでユーザー認証機能を実装しよう 2 (Rails編)

はじめに こんにちは、つよしと申します。転職のために、railsとreactでユーザー認証付きのSPAのポートフォリオを作成しました。 この記事では、Auth0 Rails(api) + React SPAでユーザー認証を実装する方法を解説します 初学者のため、間違っている情報があるかもしれません。 その場合は、ご指摘,もしくは適宜読み替えて勧めていただけたらと思います。 この記事は3部構成になっています。 【画像手順解説】Auth0 Rails(api) + React SPAでユーザー認証機能を実装しよう 2 (Rails編) → 本記事 この記事で最終的に出来ること Rails(API) + React SPAでユーザー認証機能を実装 current_user機能 Authenticate_user機能 新しいユーザーがサインアップした場合は自動でユーザーをクリエイト 個別での使用方法も学べるので、どちらか一方の実装する方も参考になると思います。 本記事で解説すること Rails(api)でのAuth0設定 current_user機能 Authenticate_user機能 新しいユーザーがサインアップした場合は自動でユーザーをクリエイト この記事でしないこと 機能を詳細に説明はせず、手順を丁寧に書いた記事にします。 参考となる記事を随所で貼るので、気になる方はそちらを御覧ください。 目次 Auth0とは? どのような機構? Rails Auth0設定 Rails アプリ設定 Auth0とは? どのような機構 Rails Auth0設定 基本的に以下の記事を参考にして制作します Rails Auth0設定 左のタブでApplication→APIsを選択し、右上のCreate APIをクリックしてください。 NameとIdentifierを入力してください。 Nameはアプリ名で大丈夫です Identifierはhttps://アプリ名-auth-apiにしておきましょう 今回はtestとしました。 signing AlgorithmはRS256のままで大丈夫です。 Identifierの値を控えておいてください。 入力できたらCreateをクリックします。 ドメインを控えます。Testタブに移動し、url(domain)を控えてください。 オレンジ枠の部分のみ控えてください。 → https://[TENANT_NAME].auth0.com/ 以上でAuth0の設定は終わりです。 Rails アプリ設定 Auth0 railsアプリを作成します console rails new api --api Gemfileに以下の3つを記述し、bundle installします gem 'dotenv-rails' gem 'jwt' gem 'rack-cors' console bundle install .envファイルを作成し、identifierとDomainを使用できるようにします。 ※ gitにpushする場合は.gitignoreに.envを記述してください console touch .env AUTH0_DOMAIN=[domain] # 例:https://hogehoge.auth0.com/ AUTH0_IDENTIFIER=[identifier] 例:https://test-auth-api ※ []はいりません。文字列のみ入力してください。 環境変数が正しく出力できるか確認します console irb(main):001:0> ENV['AUTH0_DOMAIN'] => "https://hogehoge.auth0.com/" irb(main):002:0> ENV['AUTH0_IDENTIFIER'] => "https://test-auth-api" ここから少し複雑になるので ディレクトリ構成の確認 コードをコピーしてタイプミス防止 以上の2点に気をつけてください。 各種ファイルについての役割は最後に説明します。 app配下にlibフォルダを作成します。 console mkdir app/lib app/lib/にjson_web_token.rbを作成します。 console touch app/lib/json_web_token.rb json_web_token.rbに以下を記述します。 app/lib/json_web_token.rb # app/lib/json_web_token.rb require 'net/http' require 'uri' class JsonWebToken def self.verify(token) JWT.decode(token, nil, true, # Verify the signature of this token algorithm: 'RS256', iss: ENV['AUTH0_DOMAIN'], verify_iss: true, aud: ENV['AUTH0_IDENTIFIER'], verify_aud: true) do |header| jwks_hash[header['kid']] end end def self.jwks_hash jwks_raw = Net::HTTP.get URI("#{ENV['AUTH0_DOMAIN']}.well-known/jwks.json") jwks_keys = Array(JSON.parse(jwks_raw)['keys']) Hash[ jwks_keys .map do |k| [ k['kid'], OpenSSL::X509::Certificate.new( Base64.decode64(k['x5c'].first) ).public_key ] end ] end end app配下にservicesフォルダを作成します console mkdir app/services app/services/にauthorization_service.rbを作成します console touch app/services/authorization_service.rb authorization_service.rbに以下を記述します app/services/authorization_service.rb class AuthorizationService def initialize(headers = {}) @headers = headers end def current_user @auth_payload, @auth_header = verify_token @user = User.from_token_payload(@auth_payload) end private def http_token @headers['Authorization'].split(' ').last if @headers['Authorization'].present? end def verify_token JsonWebToken.verify(http_token) end end app/controllersにsecured_controllersを作成します console touch app/controllers/secured_controller.rb securedcontroller.rbに以下を記述します app/controllers/secured_controller.rb class SecuredController < ApplicationController before_action :authorize_request private def authorize_request authorize_request = AuthorizationService.new(request.headers) @current_user = authorize_request.current_user rescue JWT::VerificationError, JWT::DecodeError render json: { errors: ['Not Authenticated'] }, status: :unauthorized end end config/initializers/wrap_parameters.rbを以下に変更 config/initializers/wrap_parameters.rb wrap_parameters format: [] 以上でAuth0に必要な設定が終わりました。 これで、以下を実装できています。 - current_user機能 - Authenticate_user機能 - 新規サインアップ時にユーザーを自動でcreate 各種ファイルの役割について軽く説明します。 json_web_token.rb HTTPリクエストのheaderに添付されたtoken情報を解析し、Auth0の情報と照らし合わせて、人間が読めるユーザー情報に変換します。 authorization_service.rb ■ def verify_token json_web_token.rbを実行し、tokenを渡しています。 ■ def current_user User.from_token_payload(@auth_payload)で、ユーザーモデルのメソッドを実行し、ユーザーcreateもしくはユーザー情報を返します。 ユーザーモデルのメソッドは後で実装します。 secured_controller.rb ApplicationControllerを継承し、すべてのコントローラー実行前にauthorize_requestを実行します。 tokenを解析した結果、ユーザー認証出来なかった場合は errors: ['Not Authenticated'] }, status: :unauthorized を返します。 これで、Authenticate_user機能(deviseとは逆ですが...) が実装できました。 Rails アプリ設定 通信確認 それでは実際にモデルを作成して、通信をしてみます。 ユーザーモデルとポストモデルを作成します。 ユーザーとポストは 1:多で構成します console rails g model User sub:string rails g model Post user:references title:string caption:text rails db:migrate user.rbに以下を記述してください。 user.rb class User < ApplicationRecord has_many :posts, dependent: :destroy def self.from_token_payload(payload) find_by(sub: payload['sub']) || create!(sub: payload['sub']) end end ■ def self.from_token_payload(payload) token情報を参照し、userが存在する場合はuserを返す、存在しない場合はuserをcreateします。 posts_controllerを作成します console rails g controller api/v1/posts posts_controller.rbを以下の様に編集します posts_controller.rb class Api::V1::PostsController < SecuredController #SecuredContollerを継承する skip_before_action :authorize_request, only: [:index,:show] def index posts = Post.all render json: posts end def show post = Post.find(params[:id]) render json: post end def create post = @current_user.posts.build(post_params) if post.save render json: post else render json: post.errors, status: :unprocessable_entity end end def destroy post = Post.find(params[:id]) post.delete end private def post_params params.permit(:title,:caption) end end ■ skip_before_action ユーザー認証をスキップするアクションを記入します。 今回はindex,showアクションのみユーザー認証をスキップします。 routes.rbを以下の様に記述します routes.rb Rails.application.routes.draw do namespace :api do namespace :v1 do resources :posts end end end いよいよAPI通信確認です!! auth0のサイトで、テスト用のtokenをコピーしましょう オレンジ枠のaccess_tokenの値のみをコピーしてください。 railsサーバーを立ち上げます console rails s 別のターミナルで立ち上げて、以下のGETリクエストを実行します。 console curl http://localhost:3000/api/v1/posts → [] postsにはまだなにも入っていないので、空のデータが帰ってきます。 以下のPOSTリクエストを実行します。 console curl -H "Content-Type: application/json" -d '{"title":"タイトル1", "caption":"説明1"}' -X POST http://localhost:3000/api/v1/posts → {"errors":["Not Authenticated"]} headerにtokenを載せていないので、エラーが帰ってきます!! ちゃんとユーザー認証出来ていますね。 それでは最後にtokenを載せてPOSTリクエストを実行します。 [ACCESS_TOKEN]のところにコピーしたtokenを挿入してください。 console curl -H "Content-Type: application/json" -H "Authorization: bearer [ACCESS_TOKEN]" -d '{"title":"タイトル1", "caption":"説明1"}' -X POST http://localhost:3000/api/v1/posts 以下の様なレスポンスが返ってくれば成功です!! rails_log User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."sub" = ? LIMIT ? [["sub", "ZGLom4PI88yF3FvKd4tctFgKldVNDxHZ@clients"], ["LIMIT", 1]] ↳ app/models/user.rb:4:in `from_token_payload' (0.1ms) begin transaction ↳ app/models/user.rb:4:in `from_token_payload' User Create (0.6ms) INSERT INTO "users" ("sub", "created_at", "updated_at") VALUES (?, ?, ?) [["sub", "ZGLom4PI88yF3FvKd4tctFgKldVNDxHZ@clients"], ["created_at", "2021-07-29 08:50:47.913221"], ["updated_at", "2021-07-29 08:50:47.913221"]] ↳ app/models/user.rb:4:in `from_token_payload' (1.5ms) commit transaction # UserがCreateされている ↳ app/models/user.rb:4:in `from_token_payload' (0.1ms) begin transaction ↳ app/controllers/api/v1/posts_controller.rb:17:in `create' Post Create (0.5ms) INSERT INTO "posts" ("user_id", "title", "caption", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["user_id", 3], ["title", "タイトル1"], ["caption", "説明1"], ["created_at", "2021-07-29 08:50:47.930796"], ["updated_at", "2021-07-29 08:50:47.930796"]] ↳ app/controllers/api/v1/posts_controller.rb:17:in `create' (1.0ms) commit transaction ↳ app/controllers/api/v1/posts_controller.rb:17:in `create' Completed 200 OK in 563ms (Views: 0.4ms | ActiveRecord: 6.5ms | Allocations: 13691 最初にUserがCreateされている PostがCreateされている 以上2点を確認してください!! もう一度GETリクエストしてみましょう。 console curl http://localhost:3000/api/v1/posts → [{"id":1,"user_id":1,"title":"タイトル1","caption":"説明1","created_at":"2021-07-29T08:57:09.782Z","updated_at":"2021-07-29T08:57:09.782Z"}] 以上の様に返ってくれば成功です!! お疲れ様でした!! Rails(api)でのAuth0を使用したユーザー認証を機能を実装することが出来ました。 次は を御覧ください~
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【画像手順解説】Auth0 Rails(api) + React SPAでユーザー認証機能を実装しよう 3 (Rails(api) + React編)

はじめに こんにちは、つよしと申します。転職のために、railsとreactでユーザー認証付きのSPAのポートフォリオを作成しました。 この記事では、Auth0 Rails(api) + React SPAでユーザー認証を実装する方法を解説します 初学者のため、間違っている情報があるかもしれません。 その場合は、ご指摘,もしくは適宜読み替えて勧めていただけたらと思います。 この記事は3部構成になっています。 【画像手順解説】Auth0 Rails(api) + React SPAでユーザー認証機能を実装しよう 3 (Rails(api) + React編) → 本記事 この記事で最終的に出来ること Rails(API) + React SPAでユーザー認証機能を実装 current_user機能 Authenticate_user機能 新しいユーザーがサインアップした場合は自動でユーザーをクリエイト 個別での使用方法も学べるので、どちらか一方の実装する方も参考になると思います。 本記事で解説すること Rails(api) + Reactでユーザー認証を実装する 簡単なRails + ReactのCRUD機能 注:part1とpart2を読んでから本記事に取り組んでください。 この記事でしないこと 機能を詳細に説明はせず、手順を丁寧に書いた記事にします。 参考となる記事を随所で貼るので、気になる方はそちらを御覧ください。 目次 Auth0とは? どのような機構? Rails アプリ設定 React アプリ設定 Auth0とは? どのような機構 Rails アプリ設定 cors.rbに以下を記述します config/initializers/cors.rb Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins 'http://localhost:3001' resource '*', headers: :any, methods: %i[get post put patch delete options head] end end React アプリ設定 .envファイルにAUDIENCEを追加します AUDIENCEの値はpart2で控えたIdentifierです。 本記事の場合は https://test-auth-apiとなります。 /.env PORT=3001 REACT_APP_AUTH0_DOMAIN=[Domain] REACT_APP_AUTH0_CLIENT_ID=[Client ID] REACT_APP_AUTH0_AUDIENCE=[Identifier] // 追加 例: https://test-auth-api REACT_APP_REST_URL="http://localhost:3000/api/v1" ※ []はいりません。文字列のみ入力してください。 index.tsxにaudienceを追加します。 src/index.tsx ... const domain: any = process.env.REACT_APP_AUTH0_DOMAIN const clientId: any = process.env.REACT_APP_AUTH0_CLIENT_ID const audience: any = process.env.REACT_APP_AUTH0_AUDIENCE // 追加 ReactDOM.render( <Auth0Provider domain={domain} clientId={clientId} audience={audience} // 追加 redirectUri={window.location.origin} > <Provider store={store}> <App /> </Provider> </Auth0Provider>, document.getElementById('root') ... 以上でAuth0の設定は終わりです。 次からはrailsとreactの通信に入っていきます。 通信の流れ 1.React側でtokenを取得 2.Rails(api)にtoken付きでリクエストを投げる 3.Rails(api)でcreate 以上のような流れで行います。 通信をするために、axiosをインストールします。 console npm install axios --save React側でtokenを取得 ここからはログインした状態で進めてください。 /App.tsx import React, { useEffect, useState } from 'react'; import './App.css'; import { useAuth0 } from "@auth0/auth0-react"; function App() { // getAccessTokenSilentlyを追加 const { isAuthenticated,loginWithRedirect,logout,getAccessTokenSilently } = useAuth0(); //追加 const [token,setToken] = useState<string>('') // 追加 useEffect(() => { const getToken = async () => { try { const accessToken = await getAccessTokenSilently({}) setToken(accessToken) } catch (e) { console.log(e.message) } } getToken() }, []) ■ getAccessTokenSilently Auth0の関数で、ログインしている場合にtokenを取得することが出来ます。 取得したtokenはuseStateのtokenに格納しています。 index railsとreactの通信がちゃんと出来ているか確認するために、postsの投稿を取得してみます。 indexアクションはskip_before_actionで指定しているので、tokenは必要ありません。 今回は簡単に、ボタンで取得する形にします。 /App.tsx import React, { useEffect, useState } from 'react'; import './App.css'; import { useAuth0 } from "@auth0/auth0-react"; import axios from 'axios'; function App() { const { isAuthenticated,loginWithRedirect,logout,getAccessTokenSilently } = useAuth0(); const [token,setToken] = useState<string>('') // 追加 const [posts,setPosts] = useState<any>() // 追加 const fetchPosts = () => { axios.get('http://localhost:3000/api/v1/posts') .then((res) => { setPosts(res.data) }) } ... return ( <div className="App"> ... //追加 <h2>投稿一覧</h2> <button onClick={fetchPosts}>投稿取得</button> {posts?.map((post :any,index:number) => <div key={index}> <p>{post.title}</p> <p>{post.caption}</p> </div> )} </div> </div> ); } export default App; ボタンをクリックして、タイトル1,説明1が表示されれば成功です。 表示されない場合は検証ツールでconsole.logやNewWork,railsのログをみて解消してください。 create それでは最後にtoken付きのリクエストを送って投稿を作成したいと思います。 /App.tsx ... // 追加 const createPosts = () => { const headers = { headers: { Authorization: token, 'Content-Type': 'application/json', } } const data = { title: 'タイトル2', caption: '説明2' } axios.post('http://localhost:3000/api/v1/posts',data,headers) } ... return ( ... //追加 <h2>投稿作成</h2> <button onClick={createPosts}>投稿作成</button> <h2>投稿一覧</h2> <button onClick={fetchPosts}>投稿取得</button> {posts?.map((post :any,index:number) => <div key={index}> <p>{post.title}</p> <p>{post.caption}</p> </div> )} </div> </div> ); } export default App; axiosに投稿データとtoken付きheaderを載せてリクエストしています。 rails.log User Create (1.7ms) INSERT INTO "users" ("sub", "created_at", "updated_at") VALUES (?, ?, ?) [["sub", "auth0|60c833837c1b260072d36e1a"], ["created_at", "2021-07-29 13:11:44.946926"], ["updated_at", "2021-07-29 13:11:44.946926"]] ↳ app/models/user.rb:4:in `from_token_payload' (1.4ms) commit transaction ↳ app/models/user.rb:4:in `from_token_payload' (0.1ms) begin transaction ↳ app/controllers/api/v1/posts_controller.rb:17:in `create' Post Create (1.1ms) INSERT INTO "posts" ("user_id", "title", "caption", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["user_id", 5], ["title", "タイトル2"], ["caption", "説明2"], ["created_at", "2021-07-29 13:11:44.968824"], ["updated_at", "2021-07-29 13:11:44.968824"]] ↳ app/controllers/api/v1/posts_controller.rb:17:in `create' 新規のユーザーなのでCreateされていますね!! もう一度投稿取得ボタンを押して、タイトル2,説明2が追加されていたら成功です!! ログアウトした状態で投稿作成するとエラーになることも確認してください。 App.tsxの全体です App.tsx import React, { useEffect, useState } from 'react'; import './App.css'; import { useAuth0 } from "@auth0/auth0-react"; import axios from 'axios'; function App() { const { isAuthenticated,loginWithRedirect,logout,getAccessTokenSilently } = useAuth0(); const [token,setToken] = useState<string>('') const [posts,setPosts] = useState<any>() const fetchPosts = () => { axios.get('http://localhost:3000/api/v1/posts') .then((res) => { setPosts(res.data) }) } const createPosts = () => { const headers = { headers: { Authorization: token, 'Content-Type': 'application/json', } } const data = { title: 'タイトル2', caption: '説明2' } axios.post('http://localhost:3000/api/v1/posts',data,headers) } useEffect(() => { const getToken = async () => { try { const accessToken = await getAccessTokenSilently({}) setToken(accessToken) } catch (e) { console.log(e.message) } } getToken() }, []) console.log(posts) return ( <div className="App"> <div style={{padding:'20px'}}> <h2>ログインボタン</h2> <button onClick={() => loginWithRedirect()}>ログイン</button> <h2>ログアウトボタン</h2> <button onClick={() => logout()}>ログアウト</button> <h2>ログイン状態</h2> { isAuthenticated ? <p>ログイン</p> : <p>ログアウト</p> } <h2>投稿作成</h2> <button onClick={createPosts}>投稿作成</button> <h2>投稿一覧</h2> <button onClick={fetchPosts}>投稿取得</button> {posts?.map((post :any,index:number) => <div key={index}> <p>{post.title}</p> <p>{post.caption}</p> </div> )} </div> </div> ); } export default App; すべてApp.tsxに記述していますが、実際にアプリにするときは - token付きheaderをreduxで管理 - 通信処理をreactQueryで管理 以上の様にするとすっきりしたコードになります。 ここまでお読み頂きありがとうございました!! Auth0を使用し、ユーザー認証付きでSPAを作成する方法について解説させていただきました。 挙動や詳細な機能については、ぜひ自分で調べてみて、色々試してみてください~
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【画像手順解説】Auth0 Rails(api) + React SPAでユーザー認証機能を実装しよう 1 (React編)

はじめに こんにちは、つよしと申します。転職のために、railsとreactでユーザー認証付きのSPAのポートフォリオを作成しました。 この記事では、Auth0 Rails(api) + React SPAでユーザー認証を実装する方法を解説します 初学者のため、間違っている情報があるかもしれません。 その場合は、ご指摘,もしくは適宜読み替えて勧めていただけたらと思います。 この記事は3部構成になっています。 【画像手順解説】Auth0 Rails(api) + React SPAでユーザー認証機能を実装しよう 1 (React編) → 本記事 この記事で最終的に出来ること Rails(API) + React SPAでユーザー認証機能を実装 current_user機能 Authenticate_user機能 新しいユーザーがサインアップした場合は自動でユーザーをクリエイト 個別での使用方法も学べるので、どちらか一方の実装する方も参考になると思います。 本記事で解説すること ReactでのAuth0導入の仕方 この記事でしないこと 機能を詳細に説明はせず、手順を丁寧に書いた記事にします。 参考となる記事を随所で貼るので、気になる方はそちらを御覧ください。 目次 Auth0とは? どのような機構? React Auth0設定 React アプリ設定 Auth0とは? どのような機構 React Auth0設定 Auth0にログインしましょう。 右上の + Create Applicationをクリックします Single Page Web Applicationsを選択します。 名前は自分の好きなものをつけてください。(今回はTest Appにします) 設定出来たらCreateをクリックします。 Settingsタブに移り、 - Domain - Client ID 以上2つを控えておいてください。 そのまま下にスクロールし、 Application URIの項目を埋めます。 Allowed Callback URLs Allowed Logout URLs Allowed Web Origins Allowed Origins (CORS) 以上の4項目に http://localhost:3001/, http://localhost:3000/ を入力。 入力完了後は、下にスクロールし、Save Changesボタンをクリックして保存してください。 以上でreactのAuth0側の設定は終わりです。 次からアプリケーションに入ります。 Quick Startタブに移り、Reactをクリックしましょう。 ReactのQuickStartがでるのでこれを参考にして制作していきます。 Reactアプリ設定 reactのアプリを作成します(reduxは任意です) console npx create-react-app frontend --template redux-typescript もしくは npx create-react-app frontend --template typescript npm startをし、ちゃんとreactが動くか確認します。 console npm start envファイルを作成し、Settingsタブで控えたDomainとClient IDを使用できるようにします。 ※ gitにpushする場合は.gitignoreに.envを記述してください console touch .env ついでにポート番号とapiのURLも記入します。 /.env PORT=3001 REACT_APP_AUTH0_DOMAIN=[Domain] REACT_APP_AUTH0_CLIENT_ID=[Client ID] REACT_APP_REST_URL="http://localhost:3000/api/v1" ※ []はいりません。文字列のみ入力してください。 reactでauth0を簡単に使うために、以下のSDKを導入します npm install @auth0/auth0-react index.tsxを編集します src/index.tsx import { Auth0Provider } from '@auth0/auth0-react' import React from 'react' import ReactDOM from 'react-dom' import { Provider } from 'react-redux' import App from './App' import { store } from './app/store' import './index.css' import * as serviceWorker from './serviceWorker' const domain: any = process.env.REACT_APP_AUTH0_DOMAIN const clientId: any = process.env.REACT_APP_AUTH0_CLIENT_ID ReactDOM.render( <Auth0Provider domain={domain} clientId={clientId} redirectUri={window.location.origin} > <Provider store={store}> <App /> </Provider> </Auth0Provider>, document.getElementById('root') ) お疲れ様です! ここまで設定は完了です!! あとはログイン機能,ログアウトを実装しましょう!! ログイン,ログアウト機能実装 App.tsxを以下のように編集します /src/App.tsx import React from 'react'; import './App.css'; import { useAuth0 } from "@auth0/auth0-react"; // Auth0の機能をインポート function App() { const { isAuthenticated,loginWithRedirect,logout } = useAuth0(); // 必要な機能をインポート return ( <div className="App"> <div style={{padding:'20px'}}> <h2>ログインボタン</h2> <button onClick={() => loginWithRedirect()}>ログイン</button> <h2>ログアウトボタン</h2> <button onClick={() => logout()}>ログアウト</button> <h2>ログイン状態</h2> { isAuthenticated ? <p>ログイン</p> : <p>ログアウト</p> } </div> </div> ); } export default App; 基本的な使い方は以下の通りです。 import { useAuth0 } from "@auth0/auth0-react"; // auth0の機能をインポート const { isAuthenticated,loginWithRedirect,logout } = useAuth0(); // 必要な機能をインポート <button onClick={() => loginWithRedirect()}>ログイン</button> // 関数を実行する npm startをして確認してみましょう。 ログインボタンを押してみます。 ※初期設定だと英語だと思います。 自分のメールアドレスとパスワードを入力して、サインアップボタンを押してください。(Googleログインでも大丈夫です) サインアップ後自動でリダイレクトします。 ログイン状態が「ログイン」になっていれば成功です。 ログアウトもできるか試してみてください。 お疲れ様です!! 以上でReact側の設定は終了です。 次は をよかったら御覧ください~
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[入門]reactでtodoリストを作成する

調べても、自分が欲している内容のreactチュートリアルがなかったので、自分で作成した。 よくあるtodoリストを作成する。 欲していたのは、以下の内容なので、それを盛り込んだチュートリアルになっている。 ・functional componentの使用 ・アロー関数の使用 ・styled-componentsの使用 atomic design, redux, ts など取り入れたかったが、モリモリになってしまうので、そこは気が向いたら。 環境構築 nodeのinstall yarnのinstall npm i --global yarn version確認 yarn --version create-react-app のinstall yarn global add create-react-app reactアプリの作成 create-react-app todoApp todoApp は作成するディレクトリ名 reactアプリの立ち上げ yarn start localhost:3000 で画面が表示される。 hello worldだけを表示できるか試しに、src/App.js を編集する src/App.js import './App.css'; function App() { return <div className="App">hello world</div>; } export default App; 画面を見ると、hello worldとだけ表示されているはず。 コードを書き換えることで、src/App.test.js でエラーがでてしまう。 ここでは、テストファイルを扱わないのでファイルごと削除してしまってよい。 アロー関数 src/App.js でコンポーネントの宣言をアロー関数に書き換える。 [アロー関数式(https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions) src/App.js import './App.css'; const App = () => { return <div className="App">hello world</div>; }; export default App; ヘッダー作成 Header コンポーネントを作成する。 src/Header.js const Header = () => { return <h1>todo app</h1>; }; export default Header; HeaderコンポーネントをApp.jsで使用する src/App.js import './App.css'; import Header from './Header'; const App = () => { return ( <> <Header /> <div className="App">hello world</div> </> ); }; export default App; <></> についてはこちら https://ja.reactjs.org/docs/fragments.html 画面を見るとヘッダーが表示されるが、スタイルがかかっていないため左によってしまう。 styled-components cssはstyled-componentsを使用していく。 styled-componentsのinstall yarn add styled components styled-componentsを使ってスタイルを定義 styled-componentsについてはこちら src/Header.js import styled from 'styled-components'; const Header = () => { return <HeaderComponent>todo app</HeaderComponent>; }; const HeaderComponent = styled.h1` text-align: center; `; export default Header; ヘッダーが中央による todoアプリ作成 todoを入力するフォームを作成する。 src/Form.js import React from 'react'; import styled from 'styled-components'; const Form = () => { return ( <Component> <div>title</div> <input name="title" type="text" /> <div>desc</div> <textarea name="desc" type="text" /> </Component> ); }; const Component = styled.div` text-align: center; margin: 30px auto; width: 400px; font-weight: bold; `; export default Form; titleとdescを入力できるフォームができた。 state 次に、フォームに今何が入力されている状態なのかをわかるようにstateを使う。 useStateをimportして、inputとtextareaにvalueとonChangeの設定をする。 useStateについてはこちら src/Form.js import React, { useState } from 'react'; import styled from 'styled-components'; const Form = () => { const [title, setTitle] = useState(''); const [desc, setDesc] = useState(''); return ( <Component> <div>title</div> <input name="title" type="text" value={title} onChange={(e) => setTitle(e.target.value)} /> <div>desc</div> <textarea name="desc" type="text" value={desc} onChange={(e) => setDesc(e.target.value)} /> </Component> ); }; const Component = styled.div` text-align: center; margin: 30px auto; width: 400px; font-weight: bold; `; export default Form; 試しに、console.log()仕込んでみて、stateに反映されているか確認 src/Form.js import React, { useState } from 'react'; import styled from 'styled-components'; const Form = () => { const [title, setTitle] = useState(''); const [desc, setDesc] = useState(''); console.log(title); // あとで消す console.log(desc); // あとで消す return ( <Component> <div>title</div> <input name="title" type="text" value={title} onChange={(e) => setTitle(e.target.value)} /> <div>desc</div> <textarea name="desc" type="text" value={desc} onChange={(e) => setDesc(e.target.value)} /> </Component> ); }; const Component = styled.div` text-align: center; margin: 30px auto; width: 400px; font-weight: bold; `; export default Form; 何か入力するたびに、consoleにログがはかれるはず。 onClickで登録処理 次に、todoを登録。登録したtodoを表示する処理 まず、登録ボタンを作成し、onClickにクリックした時の処理を記述した関数を渡す。 処理内容は、 ・todoを登録 ・formを空にする の2つだが、まだtodoの登録先を作っていないので、一旦formを空にする処理だけ記述。 src/Form.js import React, { useState } from 'react'; import styled from 'styled-components'; const Form = () => { const [title, setTitle] = useState(''); const [desc, setDesc] = useState(''); const handleSubmit = (e) => { e.preventDefault(); // 入力formを空にする setTitle(''); setDesc(''); }; return ( <Component> <div>title</div> <input name="title" type="text" value={title} onChange={(e) => setTitle(e.target.value)} /> <div>desc</div> <textarea name="desc" type="text" value={desc} onChange={(e) => setDesc(e.target.value)} /> <br /> <button onClick={handleSubmit}>todoを作成</button> </Component> ); }; const Component = styled.div` text-align: center; margin: 30px auto; width: 400px; font-weight: bold; `; export default Form; titleとdescに文字を入力してからtodoを作成を押すと、入力フォームが空になるはず。 propsで登録処理の関数を渡す todoの登録先はstateを使う。 todoリストに登録していくために、登録処理の関数を作成し、propsでFormコンポーネントに渡す。 App.js import React, { useState } from 'react'; import Header from './Header'; import Form from './Form'; const App = () => { // 登録されたtodoリスト const [todos, setTodos] = useState([ { id: 0, title: `やること`, desc: 'やることの説明' }, ]); // todoのユニークid const [id, setId] = useState(1); // todosに入力されたtodoを登録する処理 const addTodo = (todo) => { setTodos([...todos, { id, title: todo.title, desc: todo.desc }]); setId(id + 1); }; return ( <> <Header /> <Form addTodo={addTodo} /> </> ); }; export default App; Form.jsでaddTodoを使ってtodoリストに登録させる src/Form.js import React, { useState } from 'react'; import styled from 'styled-components'; const Form = (props) => { const [title, setTitle] = useState(''); const [desc, setDesc] = useState(''); const handleSubmit = (e) => { e.preventDefault(); // todoListに登録する props.addTodo({ title, desc }); // 入力formを空にする setTitle(''); setDesc(''); }; return ( <Component> <div>title</div> <input name="title" type="text" value={title} onChange={(e) => setTitle(e.target.value)} /> <div>desc</div> <textarea name="desc" type="text" value={desc} onChange={(e) => setDesc(e.target.value)} /> <br /> <button onClick={handleSubmit}>todoを作成</button> </Component> ); }; const Component = styled.div` text-align: center; margin: 30px auto; width: 400px; font-weight: bold; `; export default Form; todoリストをmapを使用して表示 これだけでは、本当に登録されているか分からないので、 登録されたtodoを表示するコンポーネントを作成する。 propsで受け取ったtodoリストを表示する。 mapを使用して、リスト分作成していく。 その際、keyを指定すること。 mapとkeyについてはこちら TodoList.js import React from 'react'; const TodoList = (props) => { return ( <ul> {props.todos.map((todo) => ( <li key={todo.id}> <span>{todo.title}</span> <br /> <p>{todo.desc}</p> </li> ))} </ul> ); }; export default TodoList; App.jsでTodoListコンポーネントにtodosを渡す。 src/App.js import React, { useState } from 'react'; import Header from './Header'; import Form from './Form'; import TodoList from './TodoList'; const App = () => { // 登録されたtodoリスト const [todos, setTodos] = useState([ { id: 0, title: `やること`, desc: 'やることの説明' }, ]); // todoのユニークid const [id, setId] = useState(1); // todosに入力されたtodoを登録する処理 const addTodo = (todo) => { setTodos([...todos, { id, title: todo.title, desc: todo.desc }]); setId(id + 1); }; return ( <> <Header /> <Form addTodo={addTodo} /> <TodoList todos={todos} /> </> ); }; export default App; 入力フォームに文字を入れて、ボタンを押すとページ下部にtodoが追加されていく。 細かく書くのは一旦ここまで。気が向いたら追記します。 想定していることは、 ・todoが完了したことがわかるように、完了ボタン用意。 ・CRUDで言うと、todoを更新、削除できるような処理。 ・styeld-components使っておきながら、全然スタイル定義していないので、スタイル整えるのも。 ・コンポーネントの切り分け。保守性・拡張性無視しているので、atomic desgin を意識した構成にリファクタ。 ・tsの取り込み。(todoリストのオブジェクトの型がないのが気持ち悪い) ・reduxの取り込み。(redux使うほどの規模ではないから難しいかも)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactでGraphQL - Apollo Client編

ReactでGraph QLを使いたい 担当サービスでGraph QLを導入する可能性があるため、React側でこれを使用するために必要なことを調べようと思いました。 とりあえず、候補として、 Apollo Client Relay の二つが上がったんですが、前者の方がドキュメントが親切なようなので、こちらをまずターゲットとします。 ただし、Relayの方も調査をしておきたいです。Reactのv18以降、suspenseでの非同期処理が本格的に導入される際、Realyがまず対応してくることと思います。(というか現時点で既にある程度対応しているっぽい?)今後の調査対象とします。 Apollo Client導入 apollo-boostは廃止されたようなので、@apollo/clientを使用します。 ApolloClient公式を参考に進めていきます。 apollo clientを公式の通り導入します。 create-react-app example-apollo yarn add @apollo/client graphql clientを作成します。 今回は、githubのGraphQL APIを使用するのでclientはgithubへのURLを使用します。 token情報が必要なので、clientにauthorizationヘッダを追加し、tokenを記載する必要があります。 githubのtokenは予め取得し、.envファイルに記載してあるので環境変数として取得できます。(githubのtokenについては後述) // index.js const httpLink = createHttpLink({ uri: "https://api.github.com/graphql", }); const authLink = setContext((_, { headers }) => { const token = process.env.REACT_APP_GITHUB_PERSONAL_ACCESS_TOKEN; return { headers: { ...headers, authorization: token ? `Bearer ${token}` : "", }, }; }); const client = new ApolloClient({ link: authLink.concat(httpLink), cache: new InMemoryCache(), }); clientを作成したら、ApolloProviderに作成したclientを渡し、ApolloProviderを親コンポーネントとします。 子のコンポーネントとして、MainContentを使っていますが、後で作ります。 <ApolloProvider client={client}> <MainContent /> </ApolloProvider> 次はqueryを作成します。 検索対象を githubのリポジトリで READMEにreactやvueやangularという語が含まれているもの を検索するようにします。 githubのドキュメントを見ながら、Explorerで以下のように試したクエリを投げてみます。 // index.js import { gql } from "@apollo/client"; export const repositoryQuery = gql` { search(type: REPOSITORY, query: "react in:readme", last: 20) { repositoryCount nodes { ... on Repository { id url name description createdAt } } } } `; これでがクエリの用意ができました。 あとは、useQueryフックに、作成したクエリを渡してやればクエリを投げてくれます。 // mainContent.js import { useQuery } from "@apollo/client"; import { repositoryQuery } from "./graphQl/query"; const MainContent = () => { const { loading, data } = useQuery(repositoryQuery); if (loading) return "..."; return ( <ul> {data.search.nodes.map((r, i) => ( <li key={i}> <a href={r.url}>{r.url}</a> </li> ))} </ul> ); }; export default MainContent; こんな感じでyarn startすると、githubのrepositoryを検索した結果が取得できます。 簡単な構成ですが、これでGraphQLのクエリを使用することができました。 これを少し発展させ、react vue angularそれぞれの語で検索できるようにしたものが↓にあります。 https://github.com/masaka-ghub/react-apollo/tree/exam_apollo githubのトークン githubのAPIは使用するためにトークンが必要です。 発行方法は アカウントアイコン→settings→Developer settings→personal access tokensと進むと、下のような画面が出るのでGenerate new tokenを押します。 あとは名前と、必要なものにチェックをつけて作成してください。(今回はrepo以下とuser以下の項目にチェックをつけましたが、用途に合わせてチェックしてください。) プロジェクトのルートに.envファイルを作成し、 REACT_APP_GITHUB_PERSONAL_ACCESS_TOKEN=<githubにtokenとして表示された文字列> と記載するとprocess.env.REACT_APP_GITHUB_PERSONAL_ACCESS_TOKENとしてコード内で使用できるようになります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】The above error occurred in the <img> componentの対処法【styled-components】

症状 Reactでstyled-componentsを使用して、画面レイアウトの修正をしていたところ、下記のエラーが発生し画面が白飛びしてしましました。 翻訳すると、「上記のエラーはコンポーネントで発生しました」となりました。 imgコンポーネントで不具合が起きているようです。 error index.js:1 The above error occurred in the <img> component: at img at O (http://localhost:3001/static/js/vendors~main.chunk.js:71248:6) at div at O (http://localhost:3001/static/js/vendors~main.chunk.js:71248:6) at div at O (http://localhost:3001/static/js/vendors~main.chunk.js:71248:6) at div at O (http://localhost:3001/static/js/vendors~main.chunk.js:71248:6) at Home (http://localhost:3001/static/js/main.chunk.js:8060:86) Home.jsx import React from "react"; import styled from "styled-components"; import userImage from "./images/user-image.jpg"; const userImageWrapper = styled.img` `; const userImage = styled.div` `; export const Home = ()=> { return ( <userImageWrapper> <userImage src={userImage} alt={userImage}></userImage> </userImageWrapper> ) } 解決方法 userImageコンポーネントにimg、その親のコンポーネントにdivのスタイルに変更することで解決しました。 imgを表示させるコンポーネントではなく、その親のWrapperコンポーネントに対して、imgタグが適用されていたため、エラーが起こっていたようです。 Home.jsx import React from "react"; import styled from "styled-components"; import userImage from "./images/user-image.jpg"; const userImageWrapper = styled.div` `; const userImage = styled.img` `; export const Home = ()=> { return ( <userImageWrapper> <userImage src={userImage} alt={userImage}></userImage> </userImageWrapper> ) }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

react覚書

react覚書 作り込まれて複雑なreactソースになすすべがないため、 100ぶんの1くらいは理解に努めるために書いてみました。 Reactの最小単位の型 ソース内容 react-lesson  └ src/  │ ├ components  │ │  App.js  │ index.js  └ index.html index.htmls <!DOCTYPE html> <html> <head> <title>React App</title> </head> <body> <!-- idを指定してください --> <div id="root"></div> <script src="bundle.js"></script> </body> </html> index.js import React from 'react'; import ReactDOM from 'react-dom'; import App from './components/App'; // 以下に指定されたコードを貼り付けてください ReactDOM.render(<App />, document.getElementById('root')); App.js import React from 'react'; class App extends React.Component { render() { return ( <div> <h1>Hello World</h1> <p>一緒にReactを学びましょう!</p> </div> ); } } コンポーネントの理解と追加(作成と表示) Language.js // Reactをインポートしてください import React from 'react'; // Languageクラスを定義してください class Language extends React.Component { render() { return( <div className='language-item'> <div className='language-name'>HTML & CSS</div> <img className='language-image' src='https://s3-ap-northeast-1.amazonaws.com/progate/shared/images/lesson/react/html.svg' /> </div> ) } } App.js import React from 'react'; // Languageコンポーネントをインポートしてください import Language from './Language' class App extends React.Component { render() { return ( <div> <h1>言語一覧</h1> <div className="language"> {/* Languageコンポーネントを呼び出してください */} <Language/> </div> </div> ); } } export default App; index.html <!DOCTYPE html> <html> <head> <link rel="stylesheet" href="stylesheet.css"> <title>React App</title> </head> <body> <div id="root"></div> <script src="bundle.js"></script> </body> </html> stylesheet.css html { font: 100% / 1.5 "Lato", "Hiragino Maru Gothic Pro", "Meiryo UI", Meiryo, "MS PGothic", sans-serif; background: #f9fbfe; } h1, h2, h3, h4, h5, h6, p, div { color: #2b546a; font-weight: normal; } h1 { font-size: 1.5rem; } body { margin: 0; padding: 0; } h1 { padding-top: 40px; text-align:center; } p { text-align:center; } .language { height: 400px; width: 810px; padding: 0 70px; margin: auto; } .language-item { width: 240px; height: 250px; background: #ffffff; margin: 15px; float: left; box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 6px, rgba(0, 0, 0, 0.12) 0px 1px 4px; border-radius: 3px; position: relative; } .language-name { position: absolute; left: 0; right: 0; top: 40px; margin: auto; text-align: center; } .language-image { position: absolute; left: 0; right: 0; bottom: 40px; margin: auto; height: auto; width: 60%; } index.js import React from 'react'; import ReactDOM from 'react-dom'; import App from './components/App'; ReactDOM.render(<App />, document.getElementById('root'));
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

全角チルダ?波ダッシュ?に殺された記録

事件はAndroidの文字入力で起きました. PCとスマホで入力文字の挙動が違う 依頼を受けてブラウザ上で縦書き画像を簡単に作れるサービスを開発していました. textareaから文字の入力を受けて,それをcanvas上にjavascriptで縦書き文字として描画するっていうシンプルなロジックのものでした. Reactをベースにして,特にこれと言ったライブラリは一切使っていませんでした. 事件はそのとき起きました 依頼者の方から, 「~が横のままになってるのでそこだけ修正お願いします~!」 と連絡がきました. これはまずいと思い,急いでPCから修正を行おうと思いましたが・・・ まったく縦のままでした・・・. いやいや依頼者の方,なんの文字を入力したんだと思いその環境を聞くと 「スマホのChromeです~!」 とのこと.ふむふむなるほどと思い私もスマホのchromeで確認すると あああああああああああああああああああああああああああああああああああああああああ(断末魔) はい,PCとスマホで挙動が違うじゃありませんか. 検証 原因はなんとなく推測がつきます. 文字コード これしかないやろと. ということでtextareaで受け取った文字を比較しました. encodeURIComponent(input) 結果 PC スマホ やった!原因合ってた!!!!!!!! あああああああああああああああああああああああああああああああああああああああ(断末魔) 修正 文字をそのまま比較していたところを文字情報で比較することにしました // 修正前 if(ch === "~"){ // } //修正後 if(["%EF%BD%9E","%E3%80%9C"].includes(encodeURIComponent(ch)){ // } 結果 スマホ GG
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TypeScriptの型

TypeScriptの型 1.基本的な型 2.Intersection Type, Union Type 3.typeof, keyof 4.ジェネリックス 1. 基本的な型 string, number, boolean型 const name: string = "hello"; const num: number = 1; const flag: boolean = false; interface(抽象型) interface NAME { first: string; last?: string | null; } const nameObj: NAME = { first: "Yamada", last: "Taro" }; interfase型:中身の実装を持たず、メンバーや型の定義だけ持つ。 指定したメンバが必ず存在する last?:はlastが存在しない可能性もあることを指す string | null はstringまたはnullであることを指す interfaseとtypeの違い interface NAME { first: string; last: string; } type PROFILE = { age: number; city: string; }; 表記の仕方が違う(=や;の有無) interfaceではオブジェクトとクラスの型だけ定義できる typeでは他の型も参照できる interfaceは拡張ができる 最近ではtypeを使う人が多くなった 参考資料 関数の場合 const func = (x: number, y: number): number => { return x + y; }; 引数と返り値の方を指定する 2. Intersection Type, Union Type Intersection Type type PROFILE = { age: number; city: string; }; type LOGIN = { username: string; password: string; }; type USER = PROFILE & LOGIN; const userA: USER = { age: 30, city: "tokyo", username: "xxx", password: "yyy", }; type USER = PROFILE & LOGIN;によってPROFILEとLOGINの両方を満たす方を生成できる Union Type // booleanかnumberのいずれか let value: boolean | number; value = true; value = 10; // numberかstringいずれかの配列 let arrayUni: (number | string)[]; arrayUni = [0, 1, 2]; // "Facebook"か"Google"か"Amazon"のいずれか let company: "Facebook" | "Google" | "Amazon"; company = "Amazon"; 3. typeof, keyof typeof let message: string = "Hi"; // typeがstringになる let message2: typeof message; let animal = { cat: "small cat", }; let newAnimal: typeof animal = { cat: "big cat", }; typeofを使うと、指定した変数の方をコピーできる JAONの複雑な型を取得するときにtypeofが便利になる keyof type KEYS = { primary: string; secondary: string; }; let key: keyof KEYS; // "primary" || "secondary" key = "primary"; keyofを使うと、指定したオブジェクトのキーからなる型を生成してくれる 4. ジェネリックス 変数の場合 type GEN<T> = { item: T; }; // T=string const gen0: GEN<string> = { item: "hello", }; // string || numberになる type GEN2<T extends string | number> = { item: T; }; ジェネリックスを使うと、抽象的に型を宣言できる。 関数の場合 function funcGEN<T>(props: T) { return { item: props }; } const gen = funcGEN<string>("test"); 型引数Tを受け取って、propsの方にはめる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React×Typescriptの環境開発で、初っ端に出たエラー

最初の壁 転職を目指している僕はある日reactとtypescriptを勉強し始めました。そんな時今までnpmを使っていましたがポートフォリオを作るにはyarnの方が良いだろうと、yarnの導入を検討した。(正直この記事を書くならコード書けって話。) npm install --global yarn ↓ yarn global add create-react-app ↓ npx create-react-app --template typescript 好きなファイル名 そしてvs codeを開きyarn startを無事にいつものreactの画面が表示される。 『よし,次はsrcの中身を消してtsxファイルの作成だ。』 しかし現実は非情なものであった。 It looks like you're trying to use TypeScript but do not have typescript installed. error Command failed with exit code 1. 翻訳してみると.... TypeScriptを使用しようとしているようですが、typescriptがインストールされていません。 『ん? 俺はファイル作成時にインストールしたんじゃないのか??』 とりあえずGoogle先生にエラー文をぶち込んで調べる。今までの勉強でわかっていたが解決策が多すぎる。本当に多すぎる。一人一人環境が違うし知らない言葉ばかり、もはや違う国に来たと言っても過言ではない。 そんな中見つけたのがこれ。 yarn add --dev typescript @types/react @types/node TS プロジェクトっぽいのにパッケージ無い時に使うらしい。 でもtsconfig.jsonはあったから中身を良い感じにいじるコマンドだと予想,するとどうでしょう。yarn startが走るではありませんか。まじで嬉しい、宝くじで10000円当たるより嬉しい。 2つ目の壁 Could not find a declaration file for module 'react-dom'. ' /Users/satoshun/sufeing/node_modules/react-dom/index.js' implicitly has an 'any' type. Try `npm i --save-dev @types/react-dom` if it exists or add a new declaration (.d.ts) file containing `declare module 'react-dom';` 『ちくしょう、またエラーかよ』 なるほど、コウメ太夫のセリフはこういう時に使うのか。頭のCPUが余計なことに使われる。 調べてみると、どうやら原因はtsconfigにあるようでした。 したがって下記をcreate-reaxct-appで生成されたtsconfig.jsonに追加するだけで、ちゃんと起動するとか。 { "compilerOptions": { "target": "es5", "lib": [ "dom", "dom.iterable", "esnext" ], "noImplicitAny": false, "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react-jsx" }, "include": [ "src" ] } tsconfig.jsonのこの位置に "noImplicitAny": false, を入れるとエラーは解消。 これでany型をエラーにしてるらしい。 なんでかわ調べてるけどまだ結論は出てないよ。 でもtsは型別で判別してくれるからなんとなく理解はできた。 エラーは自分を成長させてくれると思ってるから嫌いだけど好きだよ。 まだまだ勉強不足だけど周りの先輩エンジニアの方は優しいしほんとに環境に恵まれた感が強い。あっという間に4月だけど気合入れるしかない。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む