20220115のReactに関する記事は18件です。

React チュートリアル パート5 (map)

目標 useEffect()で取得した、JSONファイルをブラウザに表示させましょう App.jsに記述 パート4のソースコードに、追記しています 追記したコードについて説明いたしますね 【2行目】 useStateをimportします。 【8行目】 documentsという変数を準備し、useState([ ])は、この変数を配列型に初期設定しています。 【16行目】 取得したJSONファイルをdocumentsに格納します。 【25行目】 ここからが、表示させるメソッドになります。受け取ったJSONファイルは、配列型になっています。それをmap()メソッドを使って、表示させています。表示させる際も、{document.id}というようにパラメーターを設定します ブラウザで確認 簡単に表示させることができました もし出来たら、usernameやemailも表示させてみてくださいね 次回 文字に装飾をしていきましょう 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React チュートリアル パート4 (useEffectフック)

目標 ReactJsの新機能、”useEffect( )フック”を使ってみましょう ”useEffect( )フック”は、アプリケーションがレンダリングされたタイミングで動く機能になります App.jsに記述 ※前回までの、記述は一度すべて削除してください! そして、下記の様に記述します! 記述内容は説明いたしますので安心を まず、ターミナルに"npm i axios"を記述して、Axiosアプリケーションにインストールします! これは、外部からJSONファイルを読み込む機能になります。 【1行目】 axiosをimportします。 【2行目】 useEffectをimportします。 【7行目】 useEffectとaxiosを使って、外部のJSONファイルを取得します(fetchの機能と全く同じです)。下記のサイトからJSONファイルを取得しています(ご参考に)! 【11行目】 responseにJSONファイルが入って、それをコンソールに表示させています。 コンソールで確認 しっかりJSONファイルを取得できていますね 記述がとてもシンプルなので、わかりやすいですね! 次回 useEffect()で取得したデータをブラウザに表示させましょう! 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React チュートリアル パート3 (useStateフック)

目標 ReactJsの新機能、”useState( )フック”を使ってみましょう App.jsに記述 下記の様に記述します! 【1行目】 useStateをimportします 【7行目】 countというstateを設定します。これで、countを自由自在に変更させることができます! 【13行目】 {count}は、ブラウザに表示させるための記述になります。このように変数を表示させるためには、{}で囲みます。 【16行目】 buttonをクリックすると、countが1増加する機能をつけています。 【21行目】 buttonをクリックすると、countが0にする機能をつけています。 動きをみてみましょう たったこれだけの記述で簡単に、機能を実装することができました 次回 useEffect()を使います 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React Router in TypeScriptを利用してパブリックルートとプライベートルートを作成する方法

React RouterはURLとコンポーネントを紐づけるライブラリです。React Routerを利用してパブリックルートとプライベートルートを作成する方法について解説します。 認証付きのReactアプリケーションを開発する場合、パブリックルートとプライベートルートが必要になる場合があります。まず、それらが何であるかを見ていきます。 Public Routes パブリックルートは、アプリにサインイン前のみアクセスできるルートです。そのルートに該当するページコンポーネントは、例えばサインアップページ、パスワードを忘れた場合のページなどです。 Private Routes プライベートルートは、アプリにサインイン後のみアクセスできるルートです。アプリによって様々ですが、そのルートに該当するページコンポーネントは、ユーザープロファイルページ、アプリ設定ページなどです。 パブリックルートとプライベートルートの制約は、サインイン前にプライベートルートにアクセスしてはならない、サインイン後にパブリックルートにアクセスしてはならないということです。 それでは、パブリックルートとプライベートルートを作成してみましょう。 Public Routes まず、以下のようにパブリックルートの状態を処理するPublicRouteコンポーネントを作成しましょう。 PublicRoute.tsx import { memo, ReactNode, VFC } from 'react'; import { Route, Redirect, RouteProps } from 'react-router-dom'; type Props = { children: ReactNode; isAuthenticated: boolean; redirectPath: string; } & RouteProps; export const PublicRoute: VFC<Props> = memo((props) => { const { children, isAuthenticated, redirectPath, ...routeProps } = props; return ( <Route {...routeProps} render={({ location }) => ( !isAuthenticated ? (children) : ( <Redirect to={{ pathname: redirectPath, state: { from: location }} }/> ) )} /> ); }); 上記のコードでわかるように、パブリックルートコンポーネントは、children, isAuthenticated, redirectPath, ...routePropsのpropsを受け取ります。 ユーザーが認証済の場合、ユーザーはredirectPathにリダイレクトされ、未認証の場合のみパブリックルートにアクセスできます。 Private Routes 一方、プライベートルートコンポーネントはパブリックルートに似ていますが、リダイレクトの条件が違います。 ユーザーが未認証の場合、ユーザーはredirectPathにリダイレクトされ、認証済の場合のみ、プライベートルートにアクセスできます。 PrivateRoute.tsx import { memo, ReactNode, VFC } from 'react'; import { Route, Redirect, RouteProps } from 'react-router-dom'; type Props = { children: ReactNode; isAuthenticated: boolean; redirectPath: string; } & RouteProps; export const PrivateRoute: VFC<Props> = memo((props) => { const { children, isAuthenticated, redirectPath, ...routeProps } = props; return ( <Route {...routeProps} render={({ location }) => ( isAuthenticated ? (children) : ( <Redirect to={{ pathname: redirectPath, state: { from: location }} }/> ) )} /> ); }); Integrating Routes 最後に、以下のようにルートコンポーネントをRouterコンポーネントに統合しましょう。 Router.tsx import { memo, VFC } from "react"; import { Route, Switch, RouteProps } from "react-router-dom"; import { PageNotFound } from "components/pages/PageNotFound"; import { PageTop } from "components/pages/PageTop"; import { PublicRoute } from "router/PublicRoute" import { PrivateRoute } from "router/PrivateRoute"; import { useGetToken } from "hooks/user/useGetToken"; import { PageSignIn } from "components/pages/PageSignIn"; import { PageSignUp } from "components/pages/PageSignUp"; import { PageForgot } from "components/pages/PageForgot"; import { PageTodo } from "components/pages/PageTodo"; import { PageSetting } from "components/pages/PageSetting"; import { PageHealthCheck } from "components/pages/PageHealthCheck"; import { TodosProvider } from "providers/TodosProvider"; import { PageResetPassword } from "components/pages/PageResetPassword"; export type PublicRouteProps = { isAuthenticated: boolean; redirectPath: string; } & RouteProps; export type PrivateRouteProps = { isAuthenticated: boolean; redirectPath: string; } & RouteProps; export const Router: VFC = memo(() => { const { isAuthenticated } = useGetToken(); const defaultPublicRouteProps: PublicRouteProps = { isAuthenticated: isAuthenticated, redirectPath: '/todo', }; const defaultPrivateRouteProps: PrivateRouteProps = { isAuthenticated: isAuthenticated, redirectPath: '/sign_in', }; return ( <TodosProvider> <Switch> <PublicRoute {...defaultPublicRouteProps} exact path="/" sensitive > <PageTop /> </PublicRoute> <PublicRoute {...defaultPublicRouteProps} exact path="/sign_in" sensitive > <PageSignIn /> </PublicRoute> <PublicRoute {...defaultPublicRouteProps} exact path="/sign_up" sensitive > <PageSignUp /> </PublicRoute> <PublicRoute {...defaultPublicRouteProps} exact path="/user/forgot" sensitive > <PageForgot /> </PublicRoute> <PublicRoute {...defaultPublicRouteProps} exact path="/user/reset_password" sensitive > <PageResetPassword /> </PublicRoute> <PrivateRoute {...defaultPrivateRouteProps} exact path="/todo" sensitive > <PageTodo /> </PrivateRoute> <PrivateRoute {...defaultPrivateRouteProps} exact path="/user/settings" sensitive > <PageSetting /> </PrivateRoute> <Route path="*"> <PageNotFound /> </Route> </Switch> </TodosProvider> ); }); 認証ステータスisAuthenticatedはuseGetToken()関数から受け取ります。 パブリックルートのリダイレクトパスを/todo、プライベートルートのリダイレクトパスを/sign_inに設定しました。 未認証ルートをPublicRouteコンポーネントでラップし、認証済ルートをPrivateRouteコンポーネントでラップしました。 これで、パブリックルートとプライベートルートが構成されました。一致するものがない場合、PageNotFoundがレンダリングされます。 まとめ Routerコンポーネントはアプリで使用するURLとそのURLに紐ずくページコンポーネントが全て記述されています。また認証によるリダイレクト先のURLも記述されています。メンテナンス性の高いコードを実現しています。 以上、お役に立てれば幸いです。 ソースコードをGitHubへアップしました。今回解説した内容はGitHubへアップしたSingle Page Applicationの一部分です。 GitHub - myrails-react-todo-v4 参考文献 この記事は以下の情報を参考にして執筆しました。 How to Create Public And Private Routes using React Router
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React チュートリアル パート2 (Hello World)

目標 それでは、アプリケーションい”Hello World”を表示させましょう! App.jsに記述 srcフォルダにある、App.jsファイルを開きます。 ①headerタグを削除します。 ②1行目のimport logo from './logo.svg';も削除 ③h1タグを追加して、Hello Worldと記述。 Hello World が表示されます jsファイルなのですが、HTMLと同じようなに記述することができるんですね! これも、ReactJSの特徴の一つです。 次回 早速、ReactJSの新機能フックを使って簡単な、アプリケーションを作ってみます! 次回までに、下記の拡張機能のイントールをお願いします! 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactとTailwindCSSで5つ星評価のUIを実装する

React,Tailwindを使って、星で評価を投稿するUIを実現する機会があり、 どう実現するか悩み検索するも、なかなか記事が見つからなかったので、私が実際に書いたコードを元に今回記事を書いてみました。 使っている技術は 技術 バージョン React.js 17.0.38 Tailwind CSS 3.0.13 TypeScript 4.5.4 です。完成形のイメージは以下のようなものです。 星アイコンの実装 まずは星のアイコンを実装します。 ここでは、selectedがTrueなら黄色、selectedがFalseならばグレーになるようにします。   以下はReactIconというnpmを利用していますが、SVGアイコンで実装してもそれほど変わりません。 import React, { VFC } from "react"; import { AiFillStar } from "react-icons/ai"; type PropsType = { selected: boolean; size?: string; }; export const ColorizeStarIcon: VFC<PropsType> = ({ selected, size, }: PropsType) => { const color = selected ? "text-yellow-400" : "text-gray-100"; const hoverColor = selected ? "text-gray-100" : "text-yellow-400"; return ( <AiFillStar icon="fillStar" color={color} className={`hover:${hoverColor}`} size={size} /> ); }; このアイコンのポイントは、 テキストの色を 選択済みの場合は黄色、選択されていなければグレーにする Hoverされたときのテキストの色は、選択済みの場合はホバーされるとグレーになる、選択されていなければホバーされると黄色になる という この部分です。 const color = selected ? "text-yellow-400" : "text-gray-100"; const hoverColor = selected ? "text-gray-100" : "text-yellow-400"; 星を並べる 続いて上で作った星を並べていきます。 星の数(count) 選択された星の数(value) 星を変更できるか(readonly) 星の変更時の関数(onChange) をpropsで渡されるコンポーネントを作成します。 以下、コードをしまします。詳しくは下で解説します。 import React, { VFC, useState } from "react"; import { ColorizeStarIcon } from "~/components/atoms"; type PropsType = { count: number; value: number; readonly?: boolean; size?: string; onChange?: (value: number) => void; }; export const StarRatingField: VFC<PropsType> = ({ count, value, readonly = false, size, onChange, }: PropsType) => { const [hover, setHover] = useState(-1); // Hover時にも色を変更できるように、hoverされている星の数を管理します const onClick = (starNumber: number) => { if (readonly) return; onChange && onChange(starNumber); setHover(-1); }; const onHover = (starNumber: number) => { if (readonly) return; setHover(starNumber); }; const onMouseLeave = () => { if (readonly) return; setHover(-1); }; const currentValue = hover >= 0 ? hover : value; return ( <> <ul className="flex items-center space-x-1"> {[...Array(count)] // countの数だけの要素を持った配列を作成 .map((_, i) => i + 1) .map((starNumber) => ( <li title={`${starNumber} star`} key={starNumber} onClick={() => onClick(starNumber)} onMouseOver={() => onHover(starNumber)} onMouseLeave={() => onMouseLeave()} className={readonly ? "" : "cursor-pointer"} > <ColorizeStarIcon selected={starNumber <= currentValue} // currentValue以下のindexは、selectedをTrueにする size={size} /> </li> ))} </ul> </> ); }; ポイントは、 星(count)の数だけ配列を作る: [...Array(count)] 選択された星の数(value)の数だけ、先ほど作った ColorizeStarIconコンポーネントのselectedをTrueにします Hover時にも色を変更できるように、hoverされている星の数を管理します マウスがhoverされたときには、そのhoverされた星のindex+1番にhoverの数を更新します マウスのhoverが外れたとき と クリックされたとき には、そのhoverの数を-1に更新します ざっくりですが、上記のようにすると、以下のような星の評価をできるUIを実装できます。 必要になったときにご参考ください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React+Typescriptでのbuildエラー(create-react-app)

エラー内容 create-react-appで作成したTypescriptプロジェクト1をnpm run buildするとエラー TypeError: MiniCssExtractPlugin is not a constructor 原因 mini-css-extract-plugin がバージョン2.5.0にアップデートされたことでのエラー 解決方法 mini-css-extract-plugin のバージョンダウン npm i -D --save-exact mini-css-extract-plugin@2.4.5 参考URL npx create-react-app プロジェクト名 --template typescript ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React チュートリアル パート1 (create-react-app)

目標 ReactJSを使って簡単なWEBアプリケーションを作っていきます! Getting Started Create React Appとは、Reactアプリケーションを作るための一般的な方法です。 Quick Start コマンドを使って、”npx create-react-app my-app“を入力します! ディレクトリ構造 インストールが終了すると、下記のファイルが作られます! "npm start"でプリケーションを起動 VScodeのターミナルを開いて、 ”npm start”と入力すると、localhost:3000にアプリケーションが起動します! URLを確認するとlocalhost:3000になっていますね! これで、アプリケーションの起動しました! とても簡単にアプリケーションを起動することができましたね 次回  次回は、Hello World を表示させます! 参考 Create React Appについて   
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactでおみくじ作成(javascript版あり)

React自主トレ中です。 javascriptで作成したおみくじをReactで再現して同じ動作ができるようにします。 hooksは私が仕事してた頃は使ってなかったので ゼロから調べました。 github https://github.com/mhosokawa2021/omikuji_react まずはサクッとjavascript omikuji_js.html <p id="btn">おみくじを引く</p> <script> 'use strict'; var btn = document.getElementById('btn'); btn.addEventListener('click', function () { var n = Math.ceil(Math.random() * 5); if (n == 1) { this.textContent = '大吉'; } else if (n == 2) { this.textContent = '吉'; } else if (n == 3) { this.textContent = '中吉'; } else if (n == 4) { this.textContent = '小吉'; } else if (n == 5) { this.textContent = '凶'; } }); </script> cssで少し見た目を整えます。 omikuji_js.html #btn{ font-size: 13px; background-color: #ccc; cursor: pointer; text-align: center; } Reactで挙動を再現する その1 とりあえず見た目だけ移植。 index.js import ReactDOM from "react-dom"; import { App } from "./App"; ReactDOM.render(<App />, document.getElementById("root")); app.jsx import React from "react"; import "./styles.css"; export const App = () => { return ( <> <p className="btn">おみくじを引く</p> </> ); }; style.css .btn{ font-size: 13px; background-color: #ccc; cursor: pointer; text-align: center; } Reactで挙動を再現する その2 動きの部分の処理を追加していきます。 (jsxのみ更新したのでそこだけ) app.jsx import React, { useState } from "react"; import "./styles.css"; export const App = () => { const [omikuji, setOmikuji] = useState("おみくじを引く"); const onClickOmikuji = () => { var arr = ["大吉", "小吉", "中吉", "吉", "凶", "大凶"]; var index = Math.floor(Math.random() * arr.length); setOmikuji(arr[index]); }; return ( <> <p className="btn" onClick={onClickOmikuji}> {omikuji} </p> </> ); }; できました。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JestでReactのテスト中にエラー Jest Could not locate module FileName.css (mapped as identity-obj-proxy)

背景 Reactアプリのレンダリングについてのテストとして、Jestのスナップショット機能を使用している。 テスト実行時に「Jest Could not locate module FileName.css :mapped as identity-obj-proxy」というエラーメッセージが出力されたのでその解決策をまとめた。 ※「FileName.css」にはプロジェクトに存在するCSSファイル名が入る。 > myProject@0.0.1 test C:\Users\admin\Documents\myApp > jest FAIL _tests_\Home.test.js Test suite failed to run Configuration error: Could not locate module react-datepicker/dist/react-datepicker-cssmodules.css (mapped as identity-obj-proxy) Please check your ○○ for these entries: { "moduleNameMapper": { "/^.+\.(css|less)$/": "identity-obj-proxy" }, "resolver": undefined } Test Suites: 1 failed, 1 total Tests: 0 total Snapshots: 0 total Time: 1.894s Ran all test suites. npm ERR! Test failed. See above for more details. 開発言語 (バージョン情報) React.js (17.0.2) Next.js (11.1.2) TypeScript (4.4.3) Jest (27.2.2) 解決策 ターミナルで以下のコマンドを実行する。ただし実行はpackage.jsonのあるディレクトリでおこなう。 npm install --save-dev identity-obj-proxy 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactで手書き風アニメーションしたい!

はじめに こんにちは!UI/UX大好きそるとです! 突然Reactで手書き風アニメーションをしてみたくなったのでやってみました。 Reactじゃなくても使える方法なのでぜひ参考にしてみてください! 前提 僕のこんな突然の思いつきにお金を投資するのはもったいなので以下の条件でできる方法を探してみました。 課金したくない めんどくさくない カスタムできる 実現方法 1. Figmaで手書き文字を書く Figmaとは、ブラウザ上で簡単にデザインができるツールです。フリープランがあり無料で利用できるので、誰にでも手軽に導入することができます。 タブレットとタッチペンがあれば尚よしですが、無い人も最悪マウスで書くことができます。 Figmaを起動したらメニューにあるペンマークのアイコンから「Pencil」を選択して自分が好きな文字を書きます。 こんな感じ ↓ 2. 手書き文字をSVG形式でエクスポート 書いた文字を選択し、画面右下の「Export Group 1」をクリックしてSVG形式でエクスポートします。 3. エクスポートしたSVGをプロジェクトフォルダにコピー エクスポートしたSVGを自分のプロジェクトフォルダの好きな場所にコピーします。 4. vivus.jsをインストール vivus.jsは簡単に手書きSVG画像をアニメーションさせることができるライブラリです。SVG画像のパスが一つひとつ線を描いているようなアニメーションを作ることができます。 $ npm install vivus 5. 実装 作成したSVGをインポートしてvivusを実行すれば手書き風アニメーションの完成です! App.js import { useEffect } from "react"; import title from './title.svg' //手書き文字のSVG import Vivus from "vivus"; function App() { useEffect(() => { // アニメーション new Vivus('title', {duration: 300, file: title, type: "oneByOne"}); },[]) return ( <div id="title"/> ); } export default App; 完成イメージ 初期起動時のローディングアニメーションとして使用しても良いですよね 補足 今回はFigmaを使ってSVG画像を作成しましたが、Illustratorやその他のデザインツールを使用しても同じことができます。あくまで無課金で実現したかったのでFigmaを使用しました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactでブロック崩しのボールみたいなのを作る(デザイン応用例付き)

はじめに こんにちは!UI/UX大好きそるとです! Webページを華やかにしたかったので思いつきで作ってみました。 ※ブロック崩しを作るわけではないです。 実際に下記で紹介するコンポーネントを使用して作成したWebサイトを最後に載せておりますのでお楽しみに。 実装 ブロック崩しのボール的なコンポーネント 画面の横幅・縦幅いっぱいに暴れ回るボールを作成したかったのでこちらを参考に画面の横幅・縦幅を取得するカスタムフック(useWindowDimensions)を使用しております。 表示位置・動き出す方向は固定になっておりますが、引数で指定した位置・方向に動き出すことができればもっと暴れ回らせますね! Ball.js import React, { useState, useEffect } from 'react'; import { useWindowDimensions } from './useWindowDimensions' function Ball() { // 画面の縦幅・横幅取得するカスタムフック const { width, height } = useWindowDimensions(); // 横位置 const [x, setX] = useState(10); // 縦位置 const [y, setY] = useState(10); // 横移動フラグ(true:右に移動、false:左に移動) const [moveXflag, setmoveXflag] = useState(true); // 縦移動フラグ(true:下に移動、false:上に移動) const [moveYflag, setmoveYflag] = useState(false); // サイズ const ballSize = 10; useEffect(() => { // 端に行ったら方向を逆にする if (y == 0) { setmoveYflag(true); } if (x == width - ballSize) { setmoveXflag(false); } if (y == height - ballSize) { setmoveYflag(false); } if (x == 0) { setmoveXflag(true); } // 移動速度 const timeout = setTimeout(() => { if (moveXflag) { setX(x + 1); } else { setX(x - 1); } if (moveYflag) { setY(y + 1); } else { setY(y - 1); } } , 10); return () => clearTimeout(timeout); }, [x, y]) return ( <div style={{ position: "absolute", top: y, left: x, width: ballSize + "px", height: ballSize + "px", boxShadow: "0 5px 10px 0 rgba(0, 0, 0, .3)", borderRadius: "50px", background: "red" }}> </div> ); } export default Ball; 使い方 App.js import Ball from './Ball'; function App() { return ( <div> <Ball/> </div> ); } export default App; デザインに落とし込んだ実例 ファッション系のなんちゃってECサイトを作りましたが、ホーム画面が寂しくなりそうだったので画面にいくつかのボール(ランダムで商品を表示)を配置してそのボールから商品詳細に遷移できるようにしました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

debounceを使って、入力後にFetch処理(Material UI の Autocomplete で複数選択を Fetch で候補検索しながら選択していくサンプル)

初めに タイトルが説明的でかつ長い、、 debounceを使ったAutocompleteがあまりにもひどかったので、きちんとそのあたりも含めての見直し記事 前回アップしているのは削除済み こんな感じのもの ※画面側の動作確認のために、サーバー側はFastAPI側でGoogleに候補検索をかけにいっています コード App.js import { useState, useEffect } from "react"; import { TextField, Autocomplete, Stack } from "@mui/material"; import { debounce } from "lodash"; import axios from "axios"; const App = () => { // 前回のキーワード const [beforeString, setBeforeString] = useState(""); // 表示候補(本来はreduxのuseSelectorなんかでデータを管理する) const [resultOptions, setResultOptions] = useState([]); // 選択されている内容 const [autocompleteValues, setAutocompleteValues] = useState([]); // リクエストするURL const baseUrl = "http://localhost:8000/api/search/candidate/words/"; // 変更が止まった後、指定時間(ミリ秒)後処理を行う const term = 500; const debounceSearchHandler = debounce( (changeValue) => searchMethod(changeValue), term ); // 検索処理 const searchMethod = async (changeValue) => { setBeforeString(changeValue); // 内容が同じ場合は処理しない if (changeValue.length > 0 && beforeString !== changeValue) { // FetchしてAutocompleteのoptionsの内容を取得する処理 // [{title: keyword1}, {title: keyword2}, ...] が返ってくることを想定 const responce = await axios.get(`${baseUrl}${changeValue}`); setResultOptions([...responce.data]); } else { // オートコンプリートの内容初期化 setResultOptions([]); } }; // 選択データが変わったときの処理 const selectedChange = (event, selectedValue) => { // 選択されているオートコンプリート内容変更 setAutocompleteValues([...selectedValue]); // オートコンプリートの内容初期化 setResultOptions([]); }; // 選択データの内容確認 useEffect(() => { console.log("autocompleteValues", autocompleteValues); }, [autocompleteValues]); return ( <Stack sx={{ width: 500 }}> <Autocomplete multiple // 複数選択 freeSolo // 開閉をなくす options={resultOptions} onInputChange={(e, changeValue) => { debounceSearchHandler(changeValue); }} // テキスト内容変更時 onChange={selectedChange} // 選択状態変更時 renderInput={(params) => ( <TextField {...params} placeholder="候補入力" InputProps={{ ...params.InputProps, type: "search", }} // 全角入力時のオートコンプリートを出さない対策 /> )} filterOptions={(options) => options} // オートコンプリートの検索処理をなくす getOptionLabel={(option) => option.title} /> </Stack> ); }; export default App; テストのサーバー側 ※FastAPIで作成 main.py from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware import json import uvicorn import os from candidate import requestWords app = FastAPI() # クロス設定 app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.get("/api/search/candidate/words/{input}") def read_item(input: str): keyWords = requestWords.getCandidateWords(input) keyMap = [] for word in keyWords: addKeywordMap = {"title": word} keyMap.append(addKeywordMap) return keyMap if __name__ == "__main__": uvicorn.run(app) candidate/requestWords.py import pprint import requests from lxml import etree import numpy as np import pandas as pd def getCandidateWords(specWord: str): print(specWord) # 単語格納用 google_suglist = [] # オートコンプリートのデータを取得する google_r = requests.get( "https://www.google.com/complete/search", params={ "q": specWord, "hl": "ja", "ie": "utf_8", "oe": "utf_8", "output": "toolbar", }, ) google_root = etree.XML(google_r.text) google_sugs = google_root.xpath("//suggestion") google_sugstrs = [s.get("data") for s in google_sugs] for ss in google_sugstrs: google_suglist.append(ss) return google_suglist 終わりに とりあえず、動作確認大事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactでLeafletの地図ライブラリを使う

はじめに JavaScript 地図ライブラリ Leaflet 基本処理サンプルのReact版です。 Reactになると、結構考え方が違うので、忘れないようにメモ。 パッケージ追加 yarn add leaflet react-leaflet フォルダ構成 src ├ index.js ├ App.js ├ util │ └ leafletCommon.js └ mapIcon ├ icon1.png └ icon2.png フォルダ&ファイル構成のざっくり説明 util/leafletCommon.js は共通設定とか共通処理(現在地取得しかありませんが)とか mapIcon はアイコン画像を格納しているフォルダ(この辺り良いパッケージがなかった、、、) App.js 実際ページ(コンポーネント上で)上でどう使うか ※indexはデフォルトのやつです コード util/leafletCommon.js // 地図の設定 export const mapOption = { startZoom: "13", // 開始時のズーム値 maxZoom: "18", // 最大のズーム値 minZoom: "5", // 最小のズーム値 }; // 現在地(緯度経度取得)関数 export const getCurrentPosition = () => new Promise((resolve, reject) => navigator.geolocation.getCurrentPosition(resolve, reject) ); App.js import React, { useState, useEffect } from "react"; import "leaflet/dist/leaflet.css"; import Leaflet from "leaflet"; import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet"; import { mapOption, getCurrentPosition } from "./util/leafletCommon"; // 切り替え前データ const tempPlaceData1 = [ { id: "1", lat: 33.59513931435894, lng: 130.42419433593753, name: "地点A" }, { id: "2", lat: 33.59260123175435, lng: 130.41131973266604, name: "地点B" }, { id: "3", lat: 33.59517506146791, lng: 130.42694091796878, name: "地点C" }, { id: "4", lat: 33.59653344063089, lng: 130.420138835907, name: "地点D" }, { id: "5", lat: 33.592813804823924, lng: 130.42249917984012, name: "地点E" }, { id: "6", lat: 33.590849553725455, lng: 130.4186797142029, name: "地点F" }, ]; // 切り替え後データ const tempPlaceData2 = [ { id: "7", lat: 33.55513931435894, lng: 130.40419433593753, name: "地点G" }, { id: "8", lat: 33.59260123175435, lng: 130.42131973266604, name: "地点H" }, { id: "9", lat: 33.57517506146791, lng: 130.43694091796878, name: "地点I" }, { id: "10", lat: 33.58653344063089, lng: 130.390138835907, name: "地点J" }, ]; // 現在地アイコン const currentIcon = Leaflet.icon({ iconUrl: require("./mapIcon/icon1.png"), iconSize: [40, 40], }); // 場所アイコン const placeIcon = Leaflet.icon({ iconUrl: require("./mapIcon/icon2.png"), iconSize: [40, 40], }); const App = () => { // キー設定 const [mapKey, setMapKey] = useState(0); // 現在地情報 const [currentPosition, setCurrentPosition] = useState({ lat: 0, lng: 0, }); // 場所情報 const [placeData, setPlaceData] = useState([]); // 初期処理 useEffect(() => { moveCurrentPosition(); setPlaceData([...tempPlaceData1]); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // 現在地に移動 const moveCurrentPosition = async () => { const location = await getCurrentPosition(); setCurrentPosition({ ...currentPosition, lat: location.coords.latitude, lng: location.coords.longitude, }); // キーを設定して、再表示 setMapKey(new Date().getTime()); }; // 検索処理 const getLocationList = () => { // データ設定 setPlaceData([...tempPlaceData2]); // 本当はfetchとかしてデータ取ってくる // const responce = await axios.get("http://localshot:8000/api/getLocation/..."); // setPlaceData([...responce]); }; return ( <> {/* ボタン(機能操作) */} <div> <button onClick={() => moveCurrentPosition()}>現在地</button> <button onClick={() => getLocationList()}>検索</button> </div> {/* 地図表示 */} <MapContainer key={mapKey} center={currentPosition} zoom={mapOption.startZoom} style={{ height: "90vh", width: "100vw" }} > {/* 地図のタイル情報 */} <TileLayer attribution='&amp;copy <a href="http://osm.org/copyright";>OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" maxZoom={mapOption.maxZoom} minZoom={mapOption.minZoom} /> {/* 現在地情報を出力 */} <Marker position={currentPosition} icon={currentIcon}> <Popup>現在地</Popup> </Marker> {/* 場所情報を出力 */} {placeData.length > 0 ? placeData.map((item) => ( <Marker key={item.id} position={item} icon={placeIcon}> <Popup>{item.name}</Popup> </Marker> )) : null} </MapContainer> </> ); }; export default App; こんな感じ 最後に 自宅でアップすると、家の場所をさらした記事が出来上がってしまうので、危なかった、、 あと、画像のアップロード2M制限とかあったのか、、知らんかった
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Javascript/Typescript】fetch APIでmultipart/form-dataを送信したい(備忘録)

結論 Content-Typeに値を設定しない & FormDataオブジェクトをbodyにセット FormDataオブジェクトをbodyにセットすれば、fetch APIが予期に計らって、HTTPリクエストを作ってくれるようです。 サンプルコード ※ TSで書いてるので型まわりのコードも含まれています。 type contentProps = { title: string, description: string, image: Blob | null } const CreateContent = async (props: contentProps) => { const headers = new Headers({ // 略 }) const body = new FormData() body.append("title", props.title) body.append("description", props.description) if (props.image != null) { body.append("image", props.image, 'image.jpg') } const data: RequestInit = { method: 'POST', headers: headers, body: body } const response = await fetch('http://localhost:8080/contents', data) .then((response) => {return (response.json()).then((data) => {return data})}).catch((error) => { console.log(error) }) return response } 備考:NGだったパターン Content-Typeにmultipart/form-dataをセット。 Content-Typeにmultipart/form-data; boundary=-------------------[ランダム半角英数列] bodyからContent-Typeを削除する(StackOverflow) 2つ目bodyのセットの仕方によってはうまくいくかもしれない。 3つめのContent-Type削除はおそらく特殊ケース。 参考 どんぴしゃだったStackOverflowの質問 FormData#append (MDN)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TypeScript + React + Redux(Toolkit)の構成でアプリを作成しました【4】【簡易TODOアプリ】【随時更新】

開発準備 $ npx create-react-app <プロジェクト名> --template redux-typescript # or $ yarn create react-app <プロジェクト名> --template redux-typescript formatter等の設定 PrettierとESLintを基本からまとめてみた【ReactのアプリにESLintとPrettierを導入】 必要なパッケージのインストール ① node-sass $ yarn add node-sass ② react-hook-form $ yarn add react-hook-form ③ material-ui // with npm $ npm i @mui/material @emotion/react @emotion/styled // with yarn $ yarn add @mui/material @emotion/react @emotion/styled UIを構築する ① 使わないファイルを削除する App.test.tsx logo.svg setupTests.ts ② App.tsxの編集する App.tsx import React from "react"; const App: React.FC = () => { return <div>App</div>; }; export default App; ③ App.module.scssの作成する ④ src/components/header/Header.tsx と Header.module.scss を作成する src/components/header/Header.tsx import React from "react"; import { AppBar, Toolbar, Typography } from "@mui/material"; import styles from "./Header.module.scss"; const Header: React.FC = () => { return ( <div className={styles.root}> <AppBar position="static"> <Toolbar> <Typography variant="h6" className={styles.title}> News </Typography> </Toolbar> </AppBar> </div> ); }; export default Header; ⑤ App.tsx を編集する App.tsx import React from "react"; import Header from "./components/header/Header"; import styles from "./App.module.scss"; const App: React.FC = () => { return ( <div> <Header /> </div> ); }; export default App; 参考サイト 【ReduxToolkit入門】#4-1 ~簡易TODOアプリ: Redux Toolkitの理解~ 【ReduxToolkit入門】#4-2 ~簡易TODOアプリ: 開発準備編~ 【ReduxToolkit入門】#4-3 ~簡易TODOアプリ: UI構築前編~
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Google ColaboratoryでStreamlitとReactを接続する。

Streamlitではボタン、フォームなどの標準UIのみで簡単にwebアプリを作ることができます。しかし少し凝ったレイアウトやJavaScriptを使いたい場合にはReactと連携します。 Streamlit+Reactの実装例は公式のComponentsから見ることができます。 各プロジェクト画像 or タイトルからgithubのリポジトリへ行けます。すべてのプロジェクトがReactと連携しているわけではありませんが、プロジェクトと思わしきディレクトリ内にfrontendというディレクトリがあればそのプロジェクトがReactと連携している手掛かりとなります。 チュートリアル React開発環境の構築はCreate React Appで行うと簡単ですが、Streamlit.ioでも同じようなビルド環境が提供されています。 ローカル環境であれば公式通りに進めていけば苦なくチュートリアルは完了するのですが、Google Colaboratory上で完結させるために少しハマった部分があるので残しておきます。 今回はチュートリアルIntroductionのProject Setupまで進めていきたいと思います。 動作 GIF動画 ノートブック Streamlit bridges Python and React メニューバー① > ランタイム > すべてのセルを実行 で再現できるようにしています。 ソース 必要なものをインストール、ダウンロードします。今回はStreamlitとcomponent-templateだけでOKです。 コードセル !pip install streamlit !git clone https://github.com/streamlit/component-template !mv /content/component-template/template $project_path npmコマンドで依存関係をインストールします。my_component/frontend下で行う必要があります。 コードセル !cd $frontend_path &&\ npm install __init__.pyのhttp://localhost:3001をcolabのローカルホストに書き換えます。 コードセル with open(init_py_path, "r") as f: fileText = f.read() after = fileText.replace('"http://localhost:3001"', f'"{getGoogleLocalhost(react_port)}"') with open(init_py_path, "w") as f: f.write(after) streamlit run のあとに npn run startします。 コードセル !streamlit run $init_py_path & sleep 3 && npx localtunnel --port $streamlit_port &\ cd $frontend_path && npm run start your url is: https://***-***-***.loca.ltに接続します。 Clickボタンなど動作を確認できると思います。 おわりに いかがでしたでしょうか。 これでGoogle ColabratoryでのReact開発環境が整い、凝ったスクリプトを搭載したStreamlitプロジェクトもColabratory上で開発できるようになりました。 次回は作ったプロジェクトをgithubと連携しStreamlit.ioへデプロイする一連の流れをGoogle Colabratory上のみでやりたいと思います。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【devise_token_auth】認証トークンの保存先について

はじめに  本記事は、プログラミング初学者が、学習を進めていて疑問に思った点について調べた結果を備忘録も兼ねてまとめたものです。  そのため、記事の内容に誤りが含まれている可能性があります。ご容赦ください。  間違いを見つけた方は、お手数ですが、ご指摘いただけますと幸いです。 今回の疑問点  今回の疑問点は、   認証トークンの保存先について    です。  devise-token-authを用い、認証機能を実装していた際に保存先について疑問を抱き、それぞれのメリット・デメリットについて調べました。 認証トークン保存先のメリット・デメリット local storage メリット 実装が簡単 デメリット XSS脆弱性があった場合、トークンを簡単に盗むことができる Cookie メリット HttpOnly属性をtrueにすることでJavaScriptからのCookie操作を無効にできる デメリット 実装が手間 HttpOnly属性をtrueにしても完全に安全というわけではない In-Memory メリット 攻撃が困難 デメリット 利便性が低い リロードするとログアウトされる タブ間でログイン状態が継続されない 認証プラットフォーム(Auth0) メリット 安全性が高い 利便性も損なわれない デメリット ユーザーが一定数を超えるとお金がかかる まとめ 認証トークンの保存先について検索するとCookieへの保存を紹介しているページが多いように感じましたが、どの方法にもメリット・デメリットがあるため、よく吟味して決定する必要があると感じました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む