- 投稿日:2022-02-01T22:42:14+09:00
【TypeScript】TypeScriptの基本的な型について
はじめに 本記事は、プログラミング初学者が、学習を進めていて疑問に思った点について調べた結果を備忘録も兼ねてまとめたものです。 そのため、記事の内容に誤りが含まれている可能性があります。ご容赦ください。 間違いを見つけた方は、お手数ですが、ご指摘いただけますと幸いです。 TypeScriptの基本的な型について TypeScriptの型定義の方法 型を指定するためには、変数の後に:をつけ、その後ろに型を記述します。 TypeScriptの基本的な型一覧 TypeScriptの方について基本的なものは以下の通りです。 基本的な型一覧 // boolean let bool: boolean = true; // number 数値 let num: number = 10; // string 文字列 let str: string = "ABC"; // Array 配列 (2通り) // Arrayの後に配列の要素の型を記述する方法 let arr1: Array<number> = [0, 1, 2, 3]; // 配列の要素の型を記述し、その後ろに[]を記述する方法 let arr2: number[] = [0, 1, 2, 3]; // tuple (配列の要素の型を順番を含めて指定。) let tuple: [number, string] = [4, "B"]; // any (どのような型を入れてもエラーが出ない。全ての型を許可。) let any1: any = false // void (関数が何も返していないことを示す。自動で予測されるため省略可能。) const func1 = (): void => { const sample = "sample"; }; // null let null1: null = null; // undefined let undefined1: undefined = undefined; // object let obj1: object = {}; // オブジェクトの各プロパティの方を指定 let obj2: { id: number, name: string } = { id: 0, name: "田中" };
- 投稿日:2022-02-01T17:58:02+09:00
wordle風ロゴ?ジェネレータを作ってみた
(タグが必須なので「React」タグを付けていますが、本文はReactと関係ありません。ジェネレータでReactを使用しています) wordleが人気ですね。 そんな中、The New York Timesがwordleを買収したという報道がありました。 筆者が見た記事の一つでは「The New York Times」をwordle風にしてアイキャッチ画像に使っていました。(どのネットメディアだったか思い出せず&見つけられませんでした...) ↓こんな感じの画像です それで何となく面白そうだったので、勢いでジェネレータを作ってみました。 https://y-takey.github.io/wordle_logo_generator/ (repo: https://github.com/y-takey/wordle_logo_generator ) テキストエリアに適当な単語を改行区切りで入力するとwordle風にします。 アルファベットだけでなく、何でも入力可能です。 単語の文字数や単語数に制限はありません。 最終行の単語を正解として扱います。 正解の単語より文字数が長い場合は切り捨てます。 ダウンロードボタンで画像をダウンロードできます。
- 投稿日:2022-02-01T17:48:47+09:00
Next.jsのSSRでJWTをHeaderに埋め込む方法
概要 Next.jsではCSR, SSR, SSG, ISRといった色々なレンダリング方法を使い分けることが可能です。 SSGやISRはビルド時にhtmlが生成されるためパフォーマンスが最も高く、Next.jsの開発元であるVercelも推奨しています。 ただビルド時だとユーザーのリクエストを受け取れないため、例えばログイン状態でAPIを叩いてレンダリングさせたい場合などはSSGでなくSSRを使ってPre-renderingするのがいいかと思います。 今回はSSRで実際どのように認証情報(JWT)を乗せてリクエストを送るのか、Apollo Clientとaxiosの2パターンを紹介しようと思います 参考 SSG と SSR で理解する Next.js のページレンダリング 前提 JWTをサーバー側に渡すにはcookieを使います 今回はnookiesというライブラリを使います ログイン処理にて session という名前でトークンを保存しておきます page/login.tsx const emailLogin = async () => { const token = await user?.getIdToken(); const options = { maxAage: 60 * 60, secure: true, path: '/', } if (token) setCookie({ res }, 'session', token, options); }; Apollo Client page/index.tsx export default function Home({ postData }) { /* 省略 */ } export const getServerSideProps: GetServerSideProps = async (context: GetServerSidePropsContext) => { const cookies = nookies.get(context); const accessToken = cookies.session; const apolloClient = initializeApollo(null, context); //apolloClient初期化 // トークンがない場合ログイン画面にリダイレクト if (!accessToken) { return { redirect: { permanent: false, destination: '/login', }, props: {} as never, }; } const { data: postData } = await apolloClient.query({ query: GetPostDataDocument, }); return { props: { postData }, }; } apolloClient.ts let apolloClient: ApolloClient<NormalizedCacheObject>; function createApolloClient(ctx: { req: any }) { const httpLink = createHttpLink({ uri: 'https://api.service.com/', }); const authLink = setContext((req, { headers }) => { const accessToken = nookies.get(ctx).session; // JWT取り出し return { headers: { ...headers, someoneToken: accessToken || '', // Headerに埋め込み }, }; }); return new ApolloClient({ ssrMode: typeof window === 'undefined', link: authLink.concat(httpLink), cache: new InMemoryCache(), credentials: 'include', }); } export function initializeApollo(initialState = null, ctx) { const _apolloClient = apolloClient ?? createApolloClient(ctx); //このctxにcookie情報が含まれる if (initialState) { _apolloClient.cache.restore(initialState); } return _apolloClient; } ポイント getServerSideProps は、SSRのときにサーバーサイドでのみ実行されるAPIとなります。 そちらで受け取る context パラメータには Node HTTPServer の req、 res などが内包しており、クライアント側の cookie 情報を取得できます。 なので context を apollo Client の初期化時に引数で渡して、Header に格納するという流れになります。 Axios page/index.tsx /* クライアント側はApollo Clientと同様 */ export const getServerSideProps: GetServerSideProps = async (context: GetServerSidePropsContext) => { const cookies = context.req.headers.cookie; /* session=AAAAAAAAAA... */ // トークンがない場合ログイン画面にリダイレクト if (!cookies) { return { redirect: { destination: "/login", permanent: false, }, }; } const token = cookies.replace('session=', ''); // JWTのみを取り出す const res = await api.get('/posts', { headers: { "Authorization": `Bearer ${token}` } }) const postData = res.data; return { props: { postData, } }; }; api.ts /* Axios初期化 */ const api = Axios.create({ baseURL: 'https://api.service.com/', headers: { Accept: "application/json", "Content-Type": "application/json", }, }); ポイント ApolloClientと同様の流れでcontext → cookie → session(JWT) を取り出します。 今回はAPI呼び出し時にheadersを定義しているのでわかりやすいかと思います。 あとがき 自分が詰まった箇所だったのでメモがてらまとめてみました。 参考になりましたら幸いです。
- 投稿日:2022-02-01T13:20:03+09:00
【React】event属性を受け取る。
オブジェクト形式の複数の値を受け取る方法。【自分用メモ】 React勉強中のため、間違っていたらご指摘ください。 Login画面を想定 まずは全体像 import axiosInstance from "../../axios"; import React, {useState} from "react"; const Login = () => { //受け取る初期値 const initInput = { email: '', password: '' } //useStateで初期値を代入 const [state, setState] = useState(initInput) //入力されたときに発火される。 const HandleChange = (e) => { //入力された値を受け取る。 const value = e.target.value //入力フォームが複数あるため、e.target.nameでインプット要素のname属性を受け取る。 const name = e.target.name //スプレッド演算子で要素を分解し、取得したname属性を割り当ててフォームの区別をしている。 setState({...state, [name]: value}) console.log(state) } const HandleLogin = async () => { const result = await axiosInstance.post('auth/jwt/create/', state) console.log(result) } return ( <> <form action=""> <label htmlFor="email">email</label> <input id="email" name="email" type="email" value={state.email} onChange={HandleChange} /> <label htmlFor="password">password</label> <input id="password" name="password" type="password" value={state.password} onChange={HandleChange} /> <button onClick={HandleLogin}>ログイン</button> </form> </> ) } export default Login ポイント 入力フォームごとにuseStateを設定することもできるけど、コードが多くなるので行けてない。 e.target.nameで入力されたname属性ごとにsetStateに渡せるとスッキリする。 //入力されたときに発火される。 const HandleChange = (e) => { //入力された値を受け取る。 const value = e.target.value //入力フォームが複数あるため、e.target.nameでインプット要素のname属性を受け取る。 const name = e.target.name //スプレッド演算子で要素を分解し、取得したname属性を割り当ててフォームの区別をしている。 setState({...state, [name]: value}) console.log(state) もっといい方法があればご教示ください。 ありがとうございました。
- 投稿日:2022-02-01T11:29:52+09:00
vue.jsユーザーの驚く、JSファーストの世界(特にReact)
はじめまして、これまでvue.jsをメインで開発してきた自分が、最近リアクトを勉強して驚いたJSファーストについて書いてみようと思います。 ①vue.jsの最初に覚えるv-if,v-modelなどの、「v-hoge」系のvue独自の記法は、無駄なFW自体の学習コスト これ自分は、vue学んだ時にフレームワークを学ぶなら仕方ないような〜〜〜的な感じで軽く考えていました。 むしろ何も分かっていなかったので、v-modelとかよくわからんけど、JSで書くより楽なんじゃ?と思ったりしていました。 ただ、MVC(MVVM)の概念をもとに、結構無理に分割した結果、このv-hoge系の機能を実装してることを知りとても驚きました。 JSファーストでは、DX(開発者体験)を重要視するため、無理にMVCやMVVMの概念で分割するのではなく、この辺は機能ごとにコンポーネントに分割しようぜっていう考え方があります。 そのため、フレームワーク独自のお作法(例:v-hogeなど)ではなく、そのままのJSを知っていれば書ける書き方になっており、無駄なフレームワークの学習コストを上げないようになっているんですね。。。 ②HTMLと、JSが融合していてきもい!!!けど、これが真のJSファーストの姿。 vue.jsやRailsを勉強していた自分からすると、HTMLと、JSの各場所は分けたい(MVVMの責務の切り分け)をとても重視していました。 ただ、これは初見殺し的な部分が大きく、ある程度JSとHTMLを融合させて書くことで、上で書いたFW独自のルールを覚えるコストを削減し、さらに開発者のDXを上げるため、アプリケーションの機能ごとのコンポーネントの分割をよりしやすくするための工夫なのです。 つまり、MVCやMVVMの概念を無理に当てはめてコンポーネントを実装させるのではなく、機能ごとに分割したほうが良くね?という設計思想のもとReactのようなJSファーストのFEは作られていたのですね。
- 投稿日:2022-02-01T04:57:40+09:00
[初心者log]AtomicDesignについて
本記事の目的 Reactを学んでいくにつれてAtomicDesignというデザインシステムがかなり有力だという情報を得たので、自分なりにまとめていきます。 アトミックデザインとは アトミックデザインとは、階層的にデザインを構築していくデザインシステムであり、 1.Atoms 2.Molecules 3.Organisms 4.Templates 5.Pages といった階層に分けることができる。 Atoms 画面表示された際の要素として最小単位 機能を絞った単一部品を定義する MUIならPickで使用できるPropsを絞る 例)Button, Icon, Text, Title … Molecules Atomsの組み合わせ 例)Card, Box, Form, Popup … Organisms 画面表示された際の機能としての単位 Atoms, Moleculesを組み合わせる 例)Header, Calendar, Modal, CardList … templates ページのコンテナやサイドバーのwidthなど、中身のないデザイン Atoms,Molecules,Organismsを格納する 例)MainContents, MainContentsWithSideBar … pages ページそのもの 上記全てを用いて作成する。 例)UsersPage, HomePage … まとめ こうした階層的にデザインを分けることで、後々のデザインの変更がかなり楽になるというメリットが大きいと考えられます。 個人的に、MoleculesとOrganismsをどのように分類していくかというのが大いに悩んだところです。 MoleculesなしでOrganismsのみで考えてもいいかなって感じです。
- 投稿日:2022-02-01T00:34:44+09:00
5分でわかるReact.memo, useCallback, useMemo
そろそろしっかりと理解しておきたいと思い、React.memo, useCallback, useMemoについて改めて勉強しました。 こちらの記事では、メモ化の概念や詳しい作用機序については解説しません(公式ドキュメントや素晴らしい記事が既にたくさんあります)。 この記事は、React.memoやuseCallback,useMemo などのパフォーマンス最適化の為の機能を使用して、どうコードを書けばどう動くようになる(どのように差が出る)のかということを最短時間で理解できるように書いてみたいと思います。 使い所 React.memo, useCallback, useMemoなどの機能は全て、コンポーネントのレンダリングを最適化するパフォーマンスチューニングの為の機能です。 これらの機能の使用による効果が明らかに得られるのは、処理の重たい子コンポーネントによって、コンポーネントのレンダリングパフォーマンスに影響が出ているときです。 シンプルで極端な例で、このシチュエーションを確認してみましょう。 そしてその次にこれらの最適化の機能を使って、パフォーマンスの改善を行い効果を確認していきましょう。 実践1 シチュエーションの確認 今回の学習用のメチャ重い子コンポーネントをもつアプリケーションはこちらです。 App.js import { useState, useCallback } from 'react'; import Child from './Child'; function App() { const [count, setCount] = useState(0); const [inputState, setInputState] = useState(''); const addCountHandler = () => { setCount(count + 1); }; return ( <div className='App'> <div>Count: {count}</div> <input type='text' onChange={(e) => setInputState(e.target.value)} /> <Child addCountHandler={addCountHandler} /> </div> ); } export default App; Child.js function Child({ addCountHandler }) { console.log('rendered Child'); const heavyOperationWithoutMemo = () => { let output = 0; for (let i = 0; i < 500000000; i++) { output++; } return output; }; return ( <div className='child'> <h2>Child Component</h2> <p>output : {heavyOperationWithoutMemo()}</p> <button onClick={addCountHandler}>+1 from Child</button> </div> ); } export default memo(Child); 状況の確認 アプリケーションはcountというボタンが押されれば+1ずつ増えていく数字のstateと、inputStateというinput type=text の値を保持する二つのstateを持っている。 子コンポーネントに、countのstateに+1する処理を記述した関数addCountHandlerをpropsとして渡して子コンポーネントのボタンのイベントハンドラーに登録している。 子コンポーネントはレンダリングのたびにループが5億回マワる激重関数を持っている為、レンダリングがクソ重たい。 Qiita埋め込み用動画1 激重コンポーネント pic.twitter.com/tBCmUFOufn— 70ki8suda (@70ki8suda) January 31, 2022 こちらの動画では伝わりにくいかもしれないが、+1ボタンを押してstateに反映されるのに体感.5秒くらいかかっていて、inputの値を変更したときもレンダリングにかなりのラグがある。 今回処理が重くなっている原因は、子コンポーネントのレンダリングのたびに走るループ処理である。 子コンポーネントには直接関係のない親のinputStateの値が変更されるたび、子コンポーネントも再レンダリングされていて、パフォーマンスに影響を及ぼしている。 まずは、子コンポーネントを親コンポーネントの関係のない状態から切り離して、必要のないときは、再レンダリングされないようにしよう。 React.memoで子コンポーネントの再レンダリングを親から切り離す まず、React.memoを使って、子コンポーネントそのものをメモ化し、プロップスが同じときは再レンダリングされないようにする。 やることは簡単。 import { memo } from 'react'; React.memoをインポートして export default memo(Child); Childコンポーネントに処理を適用するだけ。 これで、子コンポーネントがメモ化された。 しかし、今回のケースはこれだけではinputStateの変更の度に、処理が重くなる問題が解決していない。 原因: ChildコンポーネントはaddCountHandler関数を親からpropsとして受け取っているが、このaddCountHandler関数が親コンポーネントの再レンダリングの度に再定義される為、親のinputStateの変更のたびにpropsのaddCountHandler関数が再定義され、メモ化したにも関わらずChildコンポーネントの再レンダリングが走っている useCallbackでメモ化したコンポーネントに渡す関数をメモ化する Childコンポーネントのレンダリングを親のinputStateの変更から独立させるには、propsで渡されているaddCountHandler関数をメモ化すればよい。 このときに使うのがuseCallbackフックである。 const addCountHandler = useCallback(() => { setCount(count + 1); }, [count]); この処理により、addCountHandler関数はcountのstateが変更されたときのみ再定義されるようになった(コンポーネントの他のstateの変更から独立した)。 OK!これでinputStateとChildコンポーネントの値が独立した! パフォーマンスの改善を確認してみよう! Qiita埋め込み用 親のinputStateの更新から子コンポーネントのレンダリングを切り離すことに成功 pic.twitter.com/d08Z819PGr— 70ki8suda (@70ki8suda) January 31, 2022 動画で伝わるかわからないですが、前はテキストインプットに入力する度に子コンポーネントの再レンダリングが走っていたので処理が超重たかったのですが、これで、どうやら親のテキストインプットが更新されても子コンポーネントが再レンダリングされないように状態を切り離すことに成功したようで、とてもサクサクになりました!! パチパチ!! しかし、まだパフォーマンスを解決したい問題が一つ残っています。 ボタンをクリックしてcountの値が変わりChildコンポーネントの再レンダリングの度に、5億回のループ処理が走るため、ボタンの処理は相変わらず重たい… Childコンポーネントの5億回のループ処理をメモ化したい! useMemoで不要な再計算をスキップ const heavyOperation = useMemo(() => { let output = 0; for (let i = 0; i < 500000000; i++) { output++; } return output; }, []); ChildコンポーネントのheavyOperation関数をメモ化した。 これにより、再レンダリング時は再計算をスキップしてキャッシュの値を利用できるようになった! これで、ボタンの挙動もサクサクになるか!? Qiita埋め込み用 React.memo,useCallback,useMemoでサクサクに pic.twitter.com/fQ67v3C5aG— 70ki8suda (@70ki8suda) January 31, 2022 やったー! ボタンの挙動もサクサクになりました!(動画でわかりにくいかもしれませんが) これにてパフォーマンスチューニング完了です!(* ´꒳`)ノ"お疲れ様でした♪ 最終的なコード App.js import { useState, useCallback } from 'react'; import Child from './Child'; function App() { const [count, setCount] = useState(0); const [inputState, setInputState] = useState(''); const addCountHandler = useCallback(() => { setCount(count + 1); }, [count]); return ( <div className='App'> <div>Count: {count}</div> <input type='text' onChange={(e) => setInputState(e.target.value)} /> <Child addCountHandler={addCountHandler} /> </div> ); } export default App; Child.js import { memo, useMemo } from 'react'; function Child({ addCountHandler }) { const heavyOperation = useMemo(() => { let output = 0; for (let i = 0; i < 500000000; i++) { output++; } return output; }, []); return ( <div className='child'> <h2>Child Component</h2> <p>output : {heavyOperation}</p> <button onClick={addCountHandler}>+1 from Child</button> </div> ); } export default memo(Child); 今回、5分でReact.memo, useCallback, useMemoの使い所を学ぶ!というコンセプトで記事を書かせていたきましたが、コードの方をgithubにアップしました。遅い処理のコードもコメントアウトで残してあるので、手元の環境でぜひ、さわって挙動を確かめていただけると、安心感が得られると思います。 https://github.com/70ki8suda/learn_useMemo_useCallback なお、今回の記事は、私が学習するときに見た海外のyoutubeの動画をより簡潔に簡略して文字起こししたようなものになっている為、英語に強い方、動画でサクッと学びたい方は をチェックしてください! 最後までお読みいただき、ありがとうございました!
- 投稿日:2022-02-01T00:14:48+09:00
pnpmが`SKIP_PREFLIGHT_CHECK=true`を求めてくる場合の対処法
⚠️この情報は本記事の投稿時点で有効であったものです。各自エラーメッセージやissueを確認してください。⚠️ エラーメッセージの例 ❯ pnpm build > (プロジェクト名)@0.1.0 build /home/matoruru/Code/(省略) > craco build There might be a problem with the project dependency tree. It is likely not a bug in Create React App, but something you need to fix locally. The react-scripts package provided by Create React App requires a dependency: "webpack": "4.44.2" Don't try to install it manually: your package manager does it automatically. However, a different version of webpack was detected higher up in the tree: /home/matoruru/Code/(省略)/node_modules/.pnpm/node_modules/webpack (version: 4.46.0) Manually installing incompatible versions is known to cause hard-to-debug issues. If you would prefer to ignore this check, add SKIP_PREFLIGHT_CHECK=true to an .env file in your project. That will permanently disable this message but you might encounter other issues. To fix the dependency tree, try following the steps below in the exact order: 1. Delete package-lock.json (not package.json!) and/or yarn.lock in your project folder. 2. Delete node_modules in your project folder. 3. Remove "webpack" from dependencies and/or devDependencies in the package.json file in your project folder. 4. Run npm install or yarn, depending on the package manager you use. In most cases, this should be enough to fix the problem. If this has not helped, there are a few other things you can try: 5. If you used npm, install yarn (http://yarnpkg.com/) and repeat the above steps with it instead. This may help because npm has known issues with package hoisting which may get resolved in future versions. 6. Check if /home/matoruru/Code/(省略)/node_modules/.pnpm/node_modules/webpack is outside your project directory. For example, you might have accidentally installed something in your home folder. 7. Try running npm ls webpack in your project folder. This will tell you which other package (apart from the expected react-scripts) installed webpack. If nothing else helps, add SKIP_PREFLIGHT_CHECK=true to an .env file in your project. That would permanently disable this preflight check in case you want to proceed anyway. P.S. We know this message is long but please read the steps above :-) We hope you find them helpful! ELIFECYCLE Command failed with exit code 1. エラーメッセージが指示してくれている手順を踏んでも解決には至らず・・・。 解決策 package.json内に以下の記述を追加します。 "pnpm": { "overrides": { "webpack": "4.44.2" } }, 参考にしたissue react-scripts does not get the exact webpack version #4076 私のpnpmプロジェクトでも@react/storybookを使っていたので、同じくこの部分が問題になっていたようです。 筆者について フォローしていただけると励みになります! Twitter: https://twitter.com/_matoruru GitHub: https://github.com/matoruru