20211015のReactに関する記事は11件です。

【React・Next.js】emotion/react のセットアップ

概要 CRA(Create React App)で作成したReactプロジェクトと、Next.jsプロジェクトそれぞれに対して、emotion/reactの導入方法をまとめました。 どちらもゴールは、.tsxファイルに以下のプラグマを書かなくて済む設定にすることです。 /** @jsx jsx */ /** @jsxImportSource @emotion/react */ emotionは、CSS in JS記法で書くスタイリングパッケージです。 CRA(Create React App)への導入 以下の記事の抜粋になっています。 パッケージについて詳しく知りたい方は、こちらも併せて参照してください。 プロジェクト作成 適当なプロジェクトフォルダを作成して、以下を実行します。 npx create-react-app . --template typescript --use-npm パッケージインストール 以下をインストールします。 npm i @emotion/react npm i -D @emotion/babel-plugin npm i @craco/craco tsconfig.json 設定を追加します。 tsconfig.json "compilerOptions": { "jsxImportSource": "@emotion/react" } craco.config.js ルート直下にcraco.config.jsを作成して、以下を記述します。 craco.config.js module.exports = { babel: { presets: [ [ "@babel/preset-react", { runtime: "automatic", importSource: "@emotion/react" }, ], ], plugins: ["@emotion/babel-plugin"], }, }; package.json 内容を修正します。 package.json "scripts": { "start": "craco start", "build": "craco build", "test": "craco test" } 動作確認 cssプロパティが使用できて、デバッグ(npm start)したときと、ビルド(npm run build → serve -s build)したときにスタイルが反映されていればOKです。 App.tsx import React, { VFC } from 'react'; import { css } from '@emotion/react'; export const App: VFC = () => { return ( <div css={styles.container}> <div css={styles.text}>Hello</div> </div> ) } const styles = { container: css` position: relative; width: 100vw; height: 100vh; background-color: #1e1e1e; display: flex; justify-content: center; align-items: center; `, text: css` font-size: 5rem; color: #fff; ` } Next.js への導入 以下の記事の抜粋になっています。 パッケージについて詳しく知りたい方は、こちらも併せて参照してください。 プロジェクト作成 適当なプロジェクトフォルダを作成して、以下を実行します。 npx create-next-app . --ts --use-npm パッケージインストール 以下をインストールします。 npm i @emotion/react npm i -D @emotion/babel-plugin tsconfig.json 設定を追加します。 tsconfig.json "compilerOptions": { "jsxImportSource": "@emotion/react" } .babelrc ルート直下に.babelrcを作成して、以下を記述します。 .babelrc { "presets": [ [ "next/babel", { "preset-react": { "runtime": "automatic", "importSource": "@emotion/react" } } ] ], "plugins": ["@emotion/babel-plugin"] } 動作確認 cssプロパティが使用できて、デバッグ(npm run dev)したときと、ビルド(npm run build → npm start)したときにスタイルが反映されていればOKです。 コードはCRAの場合と重複するので割愛します。 補足:nth-child を使う場合 nth-childを使う場合、さらに追加で設定が必要になるようです。 ここでは取り上げませんが、設定方法を知りたい方は、以下の記事を参考にしてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SWR Options

これはなに? SWRを使ってみて、optionsで色々と設定ができそうだなーと思ったのと、ドキュメントがあまりわかりやすくなかったので自分の言葉でまとめてみました オプション一覧 revalidateIfStale マウントした時に古いデータがあった際に自動で再検証するかどうかを制御している デフォルト: true 何回取得しても値が変わらない場合はfalseにする(そしたらSWR使う必要あるのか?とも思った) revalidateOnMount コンポーネントのマウント時に自動で再検証するかどうか デフォルト:false enable or disable automatic revalidation when component is mounted revalidateOnFocus 画面に戻った際に自動で再検証を行うかどうか デフォルト: true 強整合性が求められるのであればtrueなのかな? revalidateOnReconnect ブラウザがオンラインになった際に自動で再検証するか デフォルト: true refreshInterval ポーリング間隔の指定、単位はミリ秒 デフォルト: 無効 refreshWhenHidden 画面が表示されていない際にリフレッシュをするか ※但し、refreshIntervalが無効の場合は自動的に強制的にfalseになる デフォルト: false refreshWhenOffline ブラウザがオフラインの際にリフレッシュをするか ※但し、refreshIntervalが無効の場合は自動的に強制的にfalseになる デフォルト: false shouldRetryOnError エラー時のリトライの有無 デフォルト: true dedupingInterval 重複したリクエストを削除する期間の指定 デフォルト: 2000 focusThrottleInterval フォーカス時の再検証の期間指定 デフォルト: 5000 loadingTimeout onLoadingSlowイベントを発行するためのタイムアウト デフォルト: 3000 errorRetryInterval エラーが起きた際のリトライの間隔、単位はミリ秒 デフォルト: 5000 errorRetryCount エラー時のリトライの最大試行回数 デフォルト: なし fallback fallbackData 初期データの設定(hookによって異なる) onLoadingSlow(key, config) リクエストの読み込みに時間がかかりすぎる場合のコールバック関数
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JEST・React】JESTのSnapshot testを更新する

コマンド フロント側の更新が入り、Snapshotが変更になった際、下記のコマンドでSnapshotを更新できます。 jest --updateSnapshot 参考 JEST|スナップショットの更新
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React-TypeScriptのプロジェクトの中で背景画像を設定する方法

今回はReact-TypeScriptのプロジェクトの中で背景画像を設定する方法について書いてみました。 使用画像のインポート Main.tsx import bgimg from "./images/main.png" returnの中身の背景画像を設定したい部分にタグを挿入しクラス名を指定 return ( <div> <div className="bgImg"></div> </div> ); CSSファイルの操作 Main.module.css .bgImage { background-image: url("./images/main.png") !important; } と、こんな感じで実装することで一応背景画像を設定することができました。 もっといい方法があると思ったので、勉強していきたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React カスタムフック 覚書

React カスタムフック覚書 ////// useCustomHook.ts import { useReducer, useCallback } from 'react'; type State = { name: string; isLoading: boolean; errorMessage?: string; }; type Handlers = { onSubmit: (name: State['name']) => void; }; enum ActionTypes { OnSubmit = 'on-submit', OnSuccess = 'on-success', OnFailure = 'on-failure', } type Action = | { type: ActionTypes.OnSubmit, name: State['name'] } | { type: ActionTypes.OnSuccess } | { type: ActionTypes.OnFailure, errorMessage: Required<State['errorMessage']> }; const reducer: React.Reducer<State, Action> = (state: State, action: Action) => { const patch = (diff: Partial<State>): State => ({ ...state, ...diff }); switch (action.type) { case ActionTypes.OnSubmit: return patch({ isLoading: true, effect: { cmd: Cmd.hogeHoge, // api叩いたり args: [action.name], successActionCreator: () => ({ type: ActionTypes.OnSuccess }), failureActionCreator: (error: Error) => ({ type: ActionTypes.OnFailure, errorMessage: error.message }), }, }); case ActionTypes.OnSuccess: return patch({ isLoading: false, effect: undefined, }); case ActionTypes.OnFailure: return patch({ isLoading: false, effect: undefined, errorMessage: action.errorMessage }); } }; const createInitialState = (name: State['name']): State => ({ name, isLoading: false, }); const useCustomHook = (name: State['name']): [State, Handlers] => { const [state, dispatch] = useReducer(reducer, name, createInitialState); const handlers: Handlers = { onSubmit: useCallback((name) => { dispatch({ type: ActionTypes.OnSubmit, name }); }, []), }; return [state, handlers]; }; export default useCustomHook; ////// useCmd.ts 頭良すぎでわけわからん このコードは別の方が作成 import { useEffect, Dispatch } from 'react'; export type Effect<F, Action> = F extends (...args: infer Params) => Promise<infer Result> ? { cmd: F; args: Params; successActionCreator: (result: Result) => Action; failureActionCreator: (e: Error) => Action; } : never; const useCmd = <F, Action>(dispatch: Dispatch<Action>, effect?: Effect<F, Action>) => { useEffect(() => { if (effect) { effect.cmd(...effect.args) .then(result => dispatch(effect.successActionCreator(result))) .catch((e: Error) => dispatch(effect.failureActionCreator(e))); } }, [dispatch, effect]); }; export default useCmd;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ReactとNext.jsの学習方法】fwywd(フュード)採用一次試験をcssも危うい初心者が学習した流れ

fwywd(フュード)の採用試験に挑戦したいけど、書いてある内容が呪文にしか見えない。 React・Next.js・tailwindcss・Vercel?? JavaScriptも全くわからない初心者が学習した流れをまとめました。 プログラミング初心者の方や同じ思いをしている人の助けになると嬉しいです。 対象 試験内容を把握したい ReactやNext.jsなどがよくわからない 何から学習したらよいかわからない 試験内容の記事 用語説明 もっと噛み砕いて... React:HTMLの見た目を作れる。HTMLに変数を渡せるため表示を変えられる。 Next.js:サーバとして動かすことができる。(Reactはサーバ機能はない) Vercel:Next.jsアプリケーションを簡単にデプロイできるサービス。 デプロイ:アプリケーションをサーバー上に展開・配置して利用できるようにすること。 Tailwind CSS:CSSを記述せずに、便利で汎用的なクラスを組み合わせてレイアウトを作る。 "React"と"Next.js"の違いをもっと詳しく知りたい方は以下の記事をご覧下さい。私が参考にしたサイトです。 参考:Next.jsとReactの違い 各技術の学習方法・参考資料 1次試験前半提出までに学習した主な内容です。 JavaScript 参考サイト等を一読しましたが、JavaScriptを学習していない私には難しかったです。 ですが、Reactのチュートリアル等に沿って行っていけば実装できたため、なんとかなったため飛ばしても良いかもしれません。 【参考】 - ES2015(ES6) 入門 - Qiita React チュートリアルを最初に行ってみたのですが、途中からついていけなくなったため、一段階ずつ学べるガイドを参考にしました。 サイトから直接、オンラインエディタを使用できるので取り掛かり易いです。 ある程度、ポートフォリオを作成後、react-scroll導入を参考にして、スムーズなスクロールを実装しました。 【参考】 - 一段階ずつ学べるガイド - Reactチュートリアル - react-scroll導入 Next.jsとVercel こちらのサイト通りに進めることで、Next.jsのアプリを作成しVercelにデプロイする流れを学べます。 流れを学習後、Next.js+Tailwind CSSのプロジェクト作成方法はこちらの記事を参考にしました。 next/imageによる画像の最適化はこちらの記事も参考にしました。 【参考】 - Create a Next.js App - next/imageによる画像最適化を試してみた - Next.js (JavaScript/TypeScript) に Tailwind CSS を導入する方法 Tailwind CSS 実際に作成したプロジェクトに記述しながら学習しました。調べ方は、オープンソースのテンプレートがたくさんあるTailwind Componentから、自分が実装したいレイアウトに近いものを探し、書かれたコードをみてtailwindcssの公式サイトで一つずつ調べていきました。 【参考】 - Tailwind Component - tailwindcssの公式サイト おわり 実際にはわからない用語や内容が多くて他のサイトも色々見ましたが、自分が実際に作るためにわかりやすく、必要だと思ったものを載せています。 次回は、実際に実装する際に困った部分をピックアップして書いていきます。 少しでも、挑戦してみようとしている人の役に立てたら嬉しいです!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[React] React Router 後編

React初心者がReact Routerについて勉強した時のメモです。 後編の今回は、 クエリパメーターの値の受け渡し stateを渡すページ遷移 Linkを使わないページ遷移 404ページ です。 クエリパラメーター クエリパラメーターとはURLの末尾ついてる?name=hogehogeのような部分のこと。 Page2.jsx import { Link } from "react-router-dom"; export const Page2 = () => { return ( <div> <h1>Page2ページです。</h1> <Link to="/page2/100">URL Parameter</Link> <br /> <Link to="/page2/100?name=hogehoge">Query Parameter</Link> </div> ); }; Urlparameter.jsx import { useParams, useLocation } from "react-router-dom"; export const Urlparameter = () => { const { id } = useParams(); const { search } = useLocation(); const query = new URLSearchParams(search); return ( <div> <h1>Urlparameterページです。</h1> <p>パラメーターは {id} です</p> <p>クエリは {query.get("name")}</p> </div> ); }; useLocationを使うことでクエリパラメーターの値の受け渡しができる。 useLocation内のsearchという部分に?以降の文字列が渡ってくる。(今回の場合だとname=hogehogeの部分) searchをURLSearchParamsメソッドの引数に渡すことでgetというメソッドを使いnameを指定することでhogehogeが取得できる。 stateを渡すページ遷移 ページ遷移の時にstateを持たせる方法。 stateを受け渡すメリットは、APIでつぶやきなどの一覧ページを取得し、取得した情報(state)を持ってつぶやき詳細ページに遷移すればもう一度APIを呼ぶ必要なく情報(state)を受け渡せる。 Page1.jsx import { Link } from "react-router-dom"; export const Page1 = () => { //-------- 渡したい情報 -------------- const arr = [...Array(100).keys()]; console.log(arr); //---------------------------------- return ( <div> <h1>Page1ページです。</h1> <Link to={{ pathname: "/page1/detailA", state: arr }}>DetailA</Link> <br /> <Link to="/page1/detailB">DetailB</Link> </div> ); }; Linkの部分にpathnameと書き、パスとstateを持たせる。 Page1DetailA.jsx import { useLocation } from "react-router-dom"; export const Page1DetailA = () => { const { state } = useLocation(); console.log(state); return ( <div> <h1>Page1DetailAページです。</h1> </div> ); }; useLocationを使うことで渡ってきたstateを取得できる。 Linkを使わないページ遷移 JavaScript側でページ遷移する方法。 例えば、ボタンを押した後のページ遷移や、投稿などの処理が終わった後に起きるページ遷移。 Page1.jsx import { Link, useHistory } from "react-router-dom"; export const Page1 = () => { const arr = [...Array(100).keys()]; console.log(arr); const history = useHistory(); const onClickDetailA = () => history.push("/page1/detailA"); return ( <div> <h1>Page1ページです。</h1> <Link to={{ pathname: "/page1/detailA", state: arr }}>DetailA</Link> <br /> <Link to="/page1/detailB">DetailB</Link> <br /> <button onClick={onClickDetailA}>DetailA</button> </div> ); }; useHistoryを使う。 history.push(リンクのパス)とすることで画面遷移できる。 今回の場合はボタンを押したら、/page1/detailAに画面遷移する。 Page1DetailA import { useLocation, useHistory } from "react-router-dom"; export const Page1DetailA = () => { const { state } = useLocation(); console.log(state); const history = useHistory(); const onClickBack = () => history.goBack(); return ( <div> <h1>Page1DetailAページです。</h1> <br /> <button onClick={onClickBack}>戻る</button> </div> ); }; history.goBack()とすることでブラウザの戻るボタンと同じような戻る画面遷移ができる。 404ページ 存在しないページを作成しておく。 Page404.jsx import { Link } from "react-router-dom"; export const Page404 = () => { return ( <div> <h1>ページが見つかりません</h1> <Link to="/">TOPに戻る</Link> </div> ); }; Router import { Switch, Route } from "react-router-dom"; import { Home } from "../Home"; import { Page404 } from "../Page404"; import { Page1Route } from "./Page1Route"; import { Page2Route } from "./Page2Route"; export const Router = () => { return ( <Switch> <Route exact path="/"> <Home /> </Route> <Route path="/page1" render={({ match: { url } }) => ( <Switch> {Page1Route.map((route) => ( <Route key={route.path} exact={route.exact} path={`${url}${route.path}`} > {route.children} </Route> ))} </Switch> )} ></Route> <Route path="/page2" render={({ match: { url } }) => ( <Switch> {Page2Route.map((route) => ( <Route key={route.path} exact={route.exact} path={`${url}${route.path}`} > {route.children} </Route> ))} </Switch> )} ></Route> ------- 追加 ----------- <Route to="*"> <Page404 /> </Route> ------------------------ </Switch> ); }; 追加した部分のようにすることでどのページにも一致しない時にPage404がレンダリングされる。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[React] React Router 前編

React初心者がReact Routerについて勉強した時のメモです。 コード部分が長くなってしまったので前編後編の2回に分けます。 今回は、 基本的なページ遷移 ネストされたページ遷移 ルーティング分割 URLパラメーターの値の受け渡し です。 事前にreact-router-domをインストール。 基本的なページ遷移 import { BrowserRouter, Link, Switch, Route } from "react-router-dom"; import { Home } from "./Home"; import { Page1 } from "./Page1"; import { Page2 } from "./Page2"; export default function App() { return ( <BrowserRouter> <div className="App"> <Link to="/">Home</Link> <br /> <Link to="/page1">Page1</Link> <br /> <Link to="/page2">Page2</Link> <Switch> <Route exact path="/"> <Home /> </Route> <Route path="/page1"> <Page1 /> </Route> <Route path="/page2"> <Page2 /> </Route> </Switch> </div> </BrowserRouter> ); } BrowserRouterで全体を囲みLinkにパスを書きリンクを生成。 どのパスの時にどのコンポーネントをレンダリングするかは、Switchで全体を囲み、コンポーネントをRouteで囲みパスを指定。 exactをつけることで完全一致のみになる。 exactがないと/でレンダリングされるので、Page1 と Page2はレンダリングされずHomeのみのレンダリングになるので注意。 ネストされたページ遷移 App.js import { Home } from "./Home"; import { Page1 } from "./Page1"; import { Page1DetailA } from "./Page1DetailA"; import { Page1DetailB } from "./Page1DetailB"; import { Page2 } from "./Page2"; export default function App() { return ( <BrowserRouter> <div className="App"> <Link to="/">Home</Link> <br /> <Link to="/page1">Page1</Link> <br /> <Link to="/page2">Page2</Link> <Switch> <Route exact path="/"> <Home /> </Route> -------------------- 追加 -------------------- <Route path="/page1" render={({ match: { url } }) => ( <Switch> <Route exact path={url}> <Page1 /> </Route> <Route path={`${url}/detailA`}> <Page1DetailA /> </Route> <Route path={`${url}/detailB`}> <Page1DetailB /> </Route> </Switch> )} > </Route> -------------------- 追加 ---------------------- <Route path="/page2"> <Page2 /> </Route> </Switch> </div> </BrowserRouter> ); } Page1.jsx import { Link } from "react-router-dom"; export const Page1 = () => { return ( <div> <h1>Page1ページです。</h1> <Link to="/page1/detailA">DetailA</Link> <br /> <Link to="/page1/detailB">DetailB</Link> </div> ); }; page1/detailApage1/detailBのようにネストされたページ遷移のルーティングの仕方。 renderはデフォルトでpropsを受け取りprops内のmatchのurlを使うことでネストされたルーティング部分はpage1であることを保証している書き方にできる。 ルート定義の分割 ルーティング部分を別ファイルに切り出す。 App.jsx import { BrowserRouter, Link } from "react-router-dom"; import { Router } from "./router/Router"; export default function App() { return ( <BrowserRouter> <div className="App"> <Link to="/">Home</Link> <br /> <Link to="/page1">Page1</Link> <br /> <Link to="/page2">Page2</Link> <Router /> </div> </BrowserRouter> ); } Router.jsx import { Switch, Route } from "react-router-dom"; import { Home } from "../Home"; import { Page2 } from "../Page2"; import { Page1Route } from "./Page1Route"; export const Router = () => { return ( <Switch> <Route exact path="/"> <Home /> </Route> -----------Page1Detail部分------------------- <Route path="/page1" render={({ match: { url } }) => ( <Switch> {Page1Route.map((route) => ( <Route key={route.path} exact={route.exact} path={`${url}${route.path}`} > {route.children} </Route> ))} </Switch> )} ></Route> ------------------------------------------- <Route path="/page2"> <Page2 /> </Route> </Switch> ); }; Page1Detail部分も別ファイルに切り出す。 Page1Route.jsx import { Page1 } from "../Page1"; import { Page1DetailA } from "../Page1DetailA"; import { Page1DetailB } from "../Page1DetailB"; export const Page1Route = [ { path: "/", exact: true, children: <Page1 /> }, { path: "/detailA", exact: false, children: <Page1DetailA /> }, { path: "/detailB", exact: false, children: <Page1DetailB /> } ]; Page1Routeコンポーネントではpath、exactかどうか、レンダリングされるコンポーネントは何か、が分かるように切り出してRouterコンポーネントでmapを使うことで実現できる。 URLパラメーター URLパラメーターはパスにIDのようなパラメーターを渡す部分。 今回はPage2にIDを渡すことにする。 Page2もPage1同様に別ファイルに切り出す。 Router.jsx import { Switch, Route } from "react-router-dom"; import { Home } from "../Home"; import { Page1Route } from "./Page1Route"; import { Page2Route } from "./Page2Route"; export const Router = () => { return ( <Switch> <Route exact path="/"> <Home /> </Route> <Route path="/page1" render={({ match: { url } }) => ( <Switch> {Page1Route.map((route) => ( <Route key={route.path} exact={route.exact} path={`${url}${route.path}`} > {route.children} </Route> ))} </Switch> )} ></Route> <Route path="/page2" render={({ match: { url } }) => ( <Switch> {Page2Route.map((route) => ( <Route key={route.path} exact={route.exact} path={`${url}${route.path}`} > {route.children} </Route> ))} </Switch> )} ></Route> </Switch> ); }; Page2Route.jsx import { Page2 } from "../Page2"; import { Urlparameter } from "../UrlParameter"; export const Page2Route = [ { path: "/", exact: true, children: <Page2 /> }, { path: "/:id", exact: false, children: <Urlparameter /> } ]; pathの部分に:パラメータ名と書くことでパラメーターを受け取ることができる。 今回はIDを受け取るので:idとする。 Page2.jsx import { Link } from "react-router-dom"; export const Page2 = () => { return ( <div> <h1>Page2ページです。</h1> <Link to="/page2/100">URL Parameter</Link> </div> ); }; Link部分に今回渡す値の100を設定しておく。 UrlParameter.jsx import { useParams } from "react-router-dom"; export const Urlparameter = () => { const { id } = useParams(); return ( <div> <h1>Urlparameterページです。</h1> <p>パラメーターは {id} です</p> </div> ); }; useParamsを使うことでパラメーターの値の受け渡しができる。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[React] ReactのCSSの当て方

ReactのCSSの当て方の種類について勉強した時のメモです。 今回当てているCSSは全て以下のスタイルで統一しています。 InlineStyle export const InlineStyle = () => { const contaierStyle = { border: "solid 2px #329eff", borderRadius: "20px" padding: "8px", margin: "8px" } return ( <div style={contaierStyle}> <p>スタイル</p> </div> ) } コンポーネント内にスタイルを記述していくReactがデフォルトで提供している方法。 CSSプロパティはキャメルケース文字列のように "" で囲い , で区切る。 CSSModules import classes from "./CssModules.module.scss"; export const CssModules = () => { return ( <div className={classes.container}> <p>スタイル</p> </div> ); }; .container { border: solid 2px #329eff; border-radius: 20px; padding: 8px; margin: 8px; } CSSを別ファイルに切り出してimportする方法。 切り出すCSSファイル名にはmoduleをつける。 CSSの書き方は普通のCSSの書き方と同じ。 node-scssをインストールすることで使用可能。 Styled JSX export const StyledJsx = () => { return ( <> <div className="container"> <p>スタイル</p> </div> <style jsx="true"> {` .container { border: solid 2px #329eff; border-radius: 20px; padding: 8px; margin: 8px; } `} </style> </> ); }; JSXの中でスタイルを書いていく方法。 デフォルトだとhoverなどの疑似要素は使用できない。 Next.jsに標準搭載されている。 styled-jsxをインストールすることで使用可能。 Styled-Components import styled from "styled-components"; export const StyledComponents = () => { return ( <Container> <p>スタイル</p> </Container> ); }; const Container = styled.div` border: solid 2px #329eff; border-radius: 20px; padding: 8px; margin: 8px; `; スタイルで当てたコンポーネントを当てていく方法。 コンポーネントファイルでstyled-componentsをimportする必要がある。 styled-componentsをインストールすることで使用可能。 書き方の工夫 import styled from "styled-components"; export const StyledComponents = () => { return ( <SContainer> <STitle>スタイル</STitle> </SContainer> ); }; const SContainer = styled.div` border: solid 2px #329eff; border-radius: 20px; padding: 8px; margin: 8px; `; const STitle = styled.p` color: red; `; コンポーネント名の頭文字をSにすることでぱっと見でスタイルを当てているコンポーネントだと分かるのでこの書き方はおすすめ。 Emotion Emotionは色々な書き方がある方法。 1行目に/** @jsx jsx */と書く必要がある。 @emotion/reactと@emotion/styledをインストールする必要がある。 CSSと同じ書き方ができる方法 /** @jsx jsx */ import { jsx, css } from "@emotion/react"; export const Emotion = () => { const containerStyle = css` border: solid 2px #329eff; border-radius: 20px; padding: 8px; margin: 8px; `; return ( <div css={containerStyle}> <p>スタイル</p> </div> ); }; inline styleと似た書き方 /** @jsx jsx */ import { jsx, css } from "@emotion/react"; export const Emotion = () => { const containerStyle = css({ border: "solid 2px #329eff", borderRadius: "20px", padding: "8px", margin: "8px" }); return ( <div css={containerStyle}> <p>スタイル</p> </div> ); }; styled-componentと似たような書き方 /** @jsx jsx */ import { jsx, css } from "@emotion/react"; import styled from "@emotion/styled"; export const Emotion = () => { return ( <SContainer> <p>スタイル</p> </SContainer> ); }; const SContainer = styled.button` border: solid 2px #329eff; border-radius: 20px; padding: 8px; margin: 8px; `; 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[React]再レンダリングの仕組みと最適化について

再レンダリング最適化について勉強した時のメモです。 再レンダリングが起きる条件 stateが更新されたとき。 propsが変更されたとき 親コンポーネントが再レンダリングされた時のコンポーネント配下の子要素。 以下の場合Bが再レンダリングされるとCも再レンダリングされる。 Stateが更新された時 import { useState } from "react"; export const App = () => { console.log("Appレンダリング") const [text, setText] = useState(""); const onChangeText = (e) => setText(e.target.value); return ( <div className="App"> <input value={text} onChange={onChangeText} /> </div> ); }; この場合inputのテキストボックスに値を入れるたびにconsoleが走るのでstateが変わるたびに再レンダリングが発生している。 propsが更新された時 src/App.jsx import { useState } from "react"; import { ChildArea } from "./components/ChildArea"; export const App = () => { const [text, setText] = useState(""); const [open, setOpen] = useState(false); const onChangeText = (e) => setText(e.target.value); const onClickOpen = () => setOpen(!open); return ( <div className="App"> <input value={text} onChange={onChangeText} /> <br /> <br /> <button onClick={onClickOpen}>表示</button> <ChildArea open={open} /> </div> ); }; src/components/ChildArea.jsx export const ChildArea = (props) => { const { open } = props; console.log("ChildAreaレンダリング"); return ( <> {open ? ( <div> <p>子コンポーネント</p> </div> ) : null} </> ); }; ChildAreaにopenというpropsを渡しているのでボタンを押すたびに再レンダリングが起きている。 親コンポーネントが再レンダリングされた時 src/App.jsx import { useState } from "react"; import { ChildArea } from "./components/ChildArea"; export const App = () => { const [text, setText] = useState(""); const [open, setOpen] = useState(false); const onChangeText = (e) => setText(e.target.value); const onClickOpen = () => setOpen(!open); return ( <div className="App"> <input value={text} onChange={onChangeText} /> <br /> <br /> <button onClick={onClickOpen}>表示</button> <ChildArea open={open} /> </div> ); }; src/components/ChildArea.jsx export const ChildArea = (props) => { const { open } = props; console.log("ChildAreaレンダリング"); return ( <> {open ? ( <div> <p>子コンポーネント</p> </div> ) : null} </> ); }; この場合ChildAreaのpropsであるopenには触れていないが、親コンポーネントに再レンダリングが起きているためChildAreaにも再レンダリングが起きている。 コンポーネント最適化 memo useCallback memo(コンポーネントをmemo化) src/components/ChildArea.jsx import { memo } from "react"; export const ChildArea = memo((props) => { const { open } = props; console.log("ChildAreaレンダリング"); return ( <> {open ? ( <div> <p>子コンポーネント</p> </div> ) : null} </> ); }); 子コンポーネントをmemo化することでpropsに変更があった時のみレンダリングが起きるようにできる。 基本的にコンポーネントは全てmemo化するべき。 useCallback(関数をmemo化) src/App.jsx import { useState } from "react"; import { ChildArea } from "./components/ChildArea"; export const App = () => { const [text, setText] = useState(""); const [open, setOpen] = useState(false); const onChangeText = (e) => setText(e.target.value); const onClickOpen = () => setOpen(!open); // 追加 const onClickClose = () => setOpen(false); return ( <div className="App"> <input value={text} onChange={onChangeText} /> <br /> <br /> <button onClick={onClickOpen}>表示</button> <ChildArea open={open} onClickClose={onClickClose} /> </div> ); }; src/components/ChildArea.jsx import { memo } from "react"; export const ChildArea = memo((props) => { const { open, onClickClose } = props; console.log("ChildAreaレンダリング"); return ( <> {open ? ( <div> <p>子コンポーネント</p> </div> ) : null} <button onClick={onClickClose}>閉じる</button> </> ); }); 今までの機能に表示を閉じるボタンを実装するためにonClickCloseという関数を追加した。 その場合、先程memo化したChildAreaには再度再レンダリングが起きるようになってしまう。 なぜなら、アロー関数で書いた関数は毎回新しい関数を生成しているという判断をされてしまい、propsが違うものとして扱われ、propsが更新された場合と同様に再レンダリングが起きてしまう。 useCallbackを使う useEffectと同様に第二引数に入れた値を監視するので今回はsetOpenを指定して監視しsetOpenの処理が走った時のみレンダリングが起きる。 結果、不要な再レンダリングを防げる。 const onClickClose = useCallback(() => setOpen(false),[setOpen]); 基本コンポーネントはmemo化しmemo化したコンポーネントに関数を使う時は、 その関数にuseCallbackを使うことで不要な再レンダリングを防げるのでレンダリングを最適化できる。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

これからReact始めたい人のための今日だけでできるTODO#23 TODOアプリの作成① ローカルサーバーと用意したサーバーと通信できるようにする

TODOアプリの作成 これまで学んできたことを応用してTODOアプリを作成していきます。 少し長くなると思うので、1日で軽くできるTODOごとに記事にしていきます。 開発環境の準備 まずは開発環境を準備します。 npx create-react-app 任意のディレクトリ名 で環境を構築しましょう。 次に不要なファイルやコードを消していきます。 不要ファイル一覧 App.css App.text.js index.css logo.svg reportWebVitals.js setupTests.js 不要な記述削除 削除したファイルが読み込んでいる記述も削除していきます。 App.js  //修正前----------------------------------------------------------- import logo from './logo.svg'; import './App.css'; function App() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.js</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div> ); } //修正後----------------------------------------------------------- function App() { return ( <p>適当な文字列を入れます</p> ); } export default App; index.js //修正前----------------------------------------------------------- import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById('root') ); // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals reportWebVitals(); //修正後----------------------------------------------------------- import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById('root') ); これでファイル側の準備はOKです。 ローカルサーバーのセッティング Node.jsのライブラリJSON Serverを使います。 yarn add json-server --dev package.jsonと同じ階層にdb.jsonを作成します。 db.json { "todos":[ { "id": 1, "content": "ローカルで使えるサーバーを用意する", "done": true }, { "id": 2, "content": "サーバーで利用するデータを作成する", "done": false }, { "id": 3, "content": "用意したデータの表示を確認する", "done": false } ] } データができたらyarn startで起動させます。 次にサーバーを起動させます。 npx json-server --watch db.json --port 3100 --watchをつけるとdb.jsonの更新を監視してくれます。 --port 3100で任意のポート番号を指定できます。 http://localhost:3100/todosに接続することでdb.jsonに作成したJSONデータがブラウザに表示されると思います。 サーバーと通信できるようにする HTTP通信を行うためのNode.jsのライブラリaxiosをインストールします。 GETやPOSTのHTTPリクエストを使ってデータの取得や更新ができます。 yarn add axios インストールが終わったあと下記のように記述することで利用できます。 定義したtodoDataUrlを利用してデータの取得や更新追加を行います。 App.js import axios from 'axios'; const todoDataUrl = 'http://localhost:3100/todos';
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む