20211014のReactに関する記事は5件です。

#ReactなんでもLT会 #2に参加して

ReactなんでもLT会 #2 in LCDCに参加して 前回 ReactなんでもLT会#1 に引き続き、Reactの勉強会に参加してきました。 今回もその中から自分が気になったこと、ためになったことをピックアップしてまとめていきます。 ドメイン駆動なステート設計 最初に断っておくのですが、ステート設計の部分に関しては結果的に私の理解が追いつかなかっので、今回は「ドメイン駆動」と「ドメイン駆動設計(DDD)」について軽くまとめます。 ドメイン駆動とは ドメイン駆動(ドメイン駆動設計)とは ドメインの知識に焦点をあてた設計手法 という説明がどの本やサイトにも大体書いてあります。 では、「ドメイン」とはなんなのか。 英単語で「ドメイン」は「領域」を表す単語です。 つまりプログラムを適用する対象となる領域を指します。 ここではそのドメインの領域に何が属するかが重要になります。 すなわちプログラムでなんの問題を解決したいのかが重要になってくる分けです。 ドメインモデリング この画像が比較的わかりやすいかと思います。 解決したい事象、問題を「ドメイン」とし、ドメインに対する関連する事象などを列挙して「モデル」を形成します。 例:メモをとる 紙、ペン、聞く耳、理解する言語能力... とかですね。 次に列挙したモデルを元に継続的にソフトウェアに反映していきます。 先程の「メモをとる」という事象に対して、列挙したモデルは果たして完璧でしょうか? 紙とペンがあっても書く手が無いとメモは取れません。 このように常にモデルが完璧か否かを吟味しながらソフトウェアに反映していきます。 というようにDDDを理解するためには、前段階がすごい大変なんだなぁというのが今回の印象。 ただ、質の良いソフトウェアを作る上では避けては通れない考え方だと思いました。 今回発表していただいた方のスライドはこちらになります。 ↓ ドメイン駆動なステート設計 比較的参考になったQiita Suspense使ってみた SuspenseはReact v16.6から実装された機能で、非同期処理などでRenderを待機させたいstateがある場合に、データが揃うまでrenderをストップさせておくことができる機能です。 React official docs メリット renderを待たないで非同期処理を開始できるので早い コンポーネントの遅延ロードを簡単に実装できる デメリット 学習コストが高い Promiseで再ロードされてしまうのでuseState, useEffect, useMemoなどの値が破棄されてしまい、使用できない。 非同期処理時にエラー(reject)が吐かれると処理が止まる。 印象としては便利そうではあるものの、まだ安定板での供給ではないので実用度は低そうですが、今後のアップデートではかなり使えるものになる思います。 ReactのKeysについて Reactでリストを作った時など、よくユニークなキーを設定しないとwarningで怒られます。挙動は大概問題なく動くのですが、一応エラーなのでその理由を今回知ることができて基礎的なことではありますがいい収穫でした。 なぜユニークなキーを設定する必要があるのか Reactには変更された箇所だけを検知してそこだけ更新する機能が備わっています。 それを実現するためにKeyが使用されています。 よって、リストなど同じような要素が並んでいる場合にKeyが正しく設定されていないと全件更新することになる場合が発生し、パフォーマンスの低下につながります。 inputなどで動的に追加する場合など、要素に入っている値が正しく更新されない場合もあるので、致命的なバグにつながる場合があります。 視覚的にめちゃくちゃわかりやすいサンプル
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactで作るTodoアプリ

前回JavaScriptで作ったTodoアプリをReactで作った時のメモです。 完成イメージ 実装 JSX まずはJSXで基本的なHTML部分を書いときます。 src/App.jsx import React from "react"; import "./styles.css"; export const App = () => { return ( <> <div className="input-area"> <input placeholder="TODOを入力" /> <button>追加</button> </div> <div className="incomplete-area"> <p className="title">未完了のTODO</p> <ul> <div className="list-row"> <li>ああああ</li> <button>完了</button> <button>削除</button> </div> <div className="list-row"> <li>いいいい</li> <button>完了</button> <button>削除</button> </div> </ul> </div> <div className="complete-area"> <p className="title">完了のTODO</p> <ul> <div className="list-row"> <li>いいいい</li> <button>戻る</button> </div> </ul> </div> </> ); }; Reactの実装を意識した形にする Todoの部分をstateにして初期値を設定しておきます。 src/App.jsx import React from "react"; import { useState } from "react"; // 追加 import "./styles.css"; export const App = () => { //-----------------------追加------------------------------------- const [incompleteTodos, setIncompleteTodos] = useState([ "ああああ", "いいいい" ]); const [completeTodos, setCompleteTodos] = useState(["うううう"]); //--------------------------------------------------------------- return ( <> <div className="input-area"> <input placeholder="TODOを入力" /> <button>追加</button> </div> <div className="incomplete-area"> <p className="title">未完了のTODO</p> <ul> //-----------------変更------------------------ {incompleteTodos.map((todo) => { return ( <div key={todo} className="list-row"> <li>{todo}</li> <button>完了</button> <button>削除</button> </div> ); })} //---------------------------------------------- </div> </ul> </div> <div className="complete-area"> <p className="title">完了のTODO</p> <ul> //-----------------変更------------------------ {completeTodos.map((todo) => { return ( <div key={todo} className="list-row"> <li>{todo}</li> <button>戻る</button> </div> ); })} //---------------------------------------------- </div> </ul> </div> </> ); }; 解説 stateとして定義したincompleteTodosとcompleteTodosをmapを使ってループしています。 mapなどを使用するときはkeyの設定を忘れずに! Todo追加 src/App.jsx import React from "react"; import { useState } from "react"; import "./styles.css"; export const App = () => { const [todoText, setTodoText] = useState(""); //追加 const [incompleteTodos, setIncompleteTodos] = useState([ "ああああ", "いいいい" ]); const [completeTodos, setCompleteTodos] = useState(["うううう"]); //-----------------------追加-------------------------------- const onChangeTodoText = (e) => setTodoText(e.target.value); const onClickAdd = () => { if (todoText == "") return; const newTodos = [...incompleteTodos, todoText]; setIncompleteTodos(newTodos); setTodoText(""); }; //------------------------------------------------------------ return ( <> <div className="input-area"> //-----------------変更---------------------- <input placeholder="TODOを入力" value={todoText} onChange={onChangeTodoText} /> <button onClick={onClickAdd}>追加</button> //------------------------------------------ </div> <div className="incomplete-area"> <p className="title">未完了のTODO</p> <ul> {incompleteTodos.map((todo) => { return ( <div key={todo} className="list-row"> <li>{todo}</li> <button>完了</button> <button>削除</button> </div> ); })} </div> </ul> </div> <div className="complete-area"> <p className="title">完了のTODO</p> <ul> {completeTodos.map((todo) => { return ( <div key={todo} className="list-row"> <li>{todo}</li> <button>戻る</button> </div> ); })} </div> </ul> </div> </> ); }; 解説 Reactでinput部分はvalueにstateを持たせinputにonChangeイベントを使い以下のような関数を書くのは定番。 const onChangeTodoText = (e) => setTodoText(e.target.value); 追加ボタンにonClickイベントでTodoを追加する関数(onClickAdd)を定義してスプレッド構文を使うことで追加している。 if (todoText == "") return;この部分は空文字の場合はreturnすることで空文字投稿を防いでいる。 Todo削除 src/App.jsx import React from "react"; import { useState } from "react"; import "./styles.css"; export const App = () => { const [todoText, setTodoText] = useState(""); const [incompleteTodos, setIncompleteTodos] = useState([ "ああああ", "いいいい" ]); const [completeTodos, setCompleteTodos] = useState(["うううう"]); const onChangeTodoText = (e) => setTodoText(e.target.value); const onClickAdd = () => { if (todoText == "") return; const newTodos = [...incompleteTodos, todoText]; setIncompleteTodos(newTodos); setTodoText(""); }; //---------------追加------------------------ const onClickDelete = (index) => { const newTodos = [...incompleteTodos]; newTodos.splice(index, 1); setIncompleteTodos(newTodos); }; //------------------------------------------ return ( <> <div className="input-area"> <input placeholder="TODOを入力" value={todoText} onChange={onChangeTodoText} /> <button onClick={onClickAdd}>追加</button> </div> <div className="incomplete-area"> <p className="title">未完了のTODO</p> <ul> {incompleteTodos.map((todo,index) => { //追加 return ( <div key={todo} className="list-row"> <li>{todo}</li> <button>完了</button> <button onClick={() => onClickDelete(index)}>削除</button> //追加 </div> ); })} </div> </ul> </div> <div className="complete-area"> <p className="title">完了のTODO</p> <ul> {completeTodos.map((todo) => { return ( <div key={todo} className="list-row"> <li>{todo}</li> <button>戻る</button> </div> ); })} </div> </ul> </div> </> ); }; 解説 削除ボタンにonClickイベントでonClickDelete関数を定義。mapと関数の引数にindexを渡すことで何番目のTodoの削除ボタンが押されたかどうかを判定できる。(関数に引数を渡すときはアロー関数を使う) spliceを使って削除している。 Todo完了 src/App.jsx import React from "react"; import { useState } from "react"; import "./styles.css"; export const App = () => { const [todoText, setTodoText] = useState(""); const [incompleteTodos, setIncompleteTodos] = useState([ "ああああ", "いいいい" ]); const [completeTodos, setCompleteTodos] = useState(["うううう"]); const onChangeTodoText = (e) => setTodoText(e.target.value); const onClickAdd = () => { if (todoText == "") return; const newTodos = [...incompleteTodos, todoText]; setIncompleteTodos(newTodos); setTodoText(""); }; const newTodos = [...incompleteTodos]; newTodos.splice(index, 1); setIncompleteTodos(newTodos); }; //-------------------------追加----------------------------------------- const onClickComplete = (index) => { const newIncompleteTodos = [...incompleteTodos]; newIncompleteTodos.splice(index, 1); const newCompleteTodos = [...completeTodos, incompleteTodos[index]]; setIncompleteTodos(newIncompleteTodos); setCompleteTodos(newCompleteTodos); }; //-------------------------------------------------------------------- return ( <> <div className="input-area"> <input placeholder="TODOを入力" value={todoText} onChange={onChangeTodoText} /> <button onClick={onClickAdd}>追加</button> </div> <div className="incomplete-area"> <p className="title">未完了のTODO</p> <ul> {incompleteTodos.map((todo,index) => { return ( <div key={todo} className="list-row"> <li>{todo}</li> <button onClick={() => onClickComplete(index)}>完了</button> //追加 <button onClick={() => onClickDelete(index)}>削除</button> </div> ); })} </div> </ul> </div> <div className="complete-area"> <p className="title">完了のTODO</p> <ul> {completeTodos.map((todo) => { return ( <div key={todo} className="list-row"> <li>{todo}</li> <button>戻る</button> </div> ); })} </div> </ul> </div> </> ); }; Todo戻す src/App.jsx import React from "react"; import { useState } from "react"; import "./styles.css"; export const App = () => { const [todoText, setTodoText] = useState(""); const [incompleteTodos, setIncompleteTodos] = useState([ "ああああ", "いいいい" ]); const [completeTodos, setCompleteTodos] = useState(["うううう"]); const onChangeTodoText = (e) => setTodoText(e.target.value); const onClickAdd = () => { if (todoText == "") return; const newTodos = [...incompleteTodos, todoText]; setIncompleteTodos(newTodos); setTodoText(""); }; const newTodos = [...incompleteTodos]; newTodos.splice(index, 1); setIncompleteTodos(newTodos); }; const onClickComplete = (index) => { const newIncompleteTodos = [...incompleteTodos]; newIncompleteTodos.splice(index, 1); const newCompleteTodos = [...completeTodos, incompleteTodos[index]]; setIncompleteTodos(newIncompleteTodos); setCompleteTodos(newCompleteTodos); }; //-------------------------追加----------------------------------------- const onClickBack = (index) => { const newCompleteTodos = [...completeTodos]; newCompleteTodos.splice(index, 1); const newIncompleteTodos = [...incompleteTodos, completeTodos[index]]; setCompleteTodos(newCompleteTodos); setIncompleteTodos(newIncompleteTodos); }; //-------------------------------------------------------------------- return ( <> <div className="input-area"> <input placeholder="TODOを入力" value={todoText} onChange={onChangeTodoText} /> <button onClick={onClickAdd}>追加</button> </div> <div className="incomplete-area"> <p className="title">未完了のTODO</p> <ul> {incompleteTodos.map((todo,index) => { return ( <div key={todo} className="list-row"> <li>{todo}</li> <button onClick={() => onClickComplete(index)}>完了</button> <button onClick={() => onClickDelete(index)}>削除</button> </div> ); })} </div> </ul> </div> <div className="complete-area"> <p className="title">完了のTODO</p> <ul> {completeTodos.map((todo, index) => { //追加 return ( <div key={todo} className="list-row"> <li>{todo}</li> <button onClick={() => onClickBack(index)}>戻る</button> // 追加 </div> ); })} </div> </ul> </div> </> ); }; 解説 完了の処理は 未完了リストから削除 → 完了リストに追加 戻るの処理はその逆なので今までの追加と削除の処理を組み合わせることで実装できる。 コンポーネント分割 最後にコンポーネント分割して完成。 JSX src/App.jsx import React from "react"; import { useState } from "react"; import "./styles.css"; export const App = () => { const [todoText, setTodoText] = useState(""); const [incompleteTodos, setIncompleteTodos] = useState([ "ああああ", "いいいい" ]); const [completeTodos, setCompleteTodos] = useState(["うううう"]); const onChangeTodoText = (e) => setTodoText(e.target.value); const onClickAdd = () => { if (todoText == "") return; const newTodos = [...incompleteTodos, todoText]; setIncompleteTodos(newTodos); setTodoText(""); }; const newTodos = [...incompleteTodos]; newTodos.splice(index, 1); setIncompleteTodos(newTodos); }; const onClickComplete = (index) => { const newIncompleteTodos = [...incompleteTodos]; newIncompleteTodos.splice(index, 1); const newCompleteTodos = [...completeTodos, incompleteTodos[index]]; setIncompleteTodos(newIncompleteTodos); setCompleteTodos(newCompleteTodos); }; const onClickBack = (index) => { const newCompleteTodos = [...completeTodos]; newCompleteTodos.splice(index, 1); const newIncompleteTodos = [...incompleteTodos, completeTodos[index]]; setCompleteTodos(newCompleteTodos); setIncompleteTodos(newIncompleteTodos); }; return ( <> //-----------------------------変更------------------------------------------ <InputTodo todoText={todoText} onChange={onChangeTodoText} onClick={onClickAdd} /> <IncompleteTodos todos={incompleteTodos} onClickComplete={onClickComplete} onClickDelete={onClickDelete} /> <CompleteTodos completeTodos={completeTodos} onClickBack={onClickBack} /> //------------------------------------------------------------------------- </> ); }; 完了リスト src/components/CompleteTodos.jsx import React from "react"; export const CompleteTodos = (props) => { const { completeTodos, onClickBack } = props; return ( <div className="complete-area"> <p className="title">完了のTODO</p> <ul> {completeTodos.map((todo, index) => { return ( <div key={todo} className="list-row"> <li>{todo}</li> <button onClick={() => onClickBack(index)}>戻る</button> </div> ); })} </ul> </div> ); }; 未完了リスト src/components/IncompleteTodos.jsx import React from "react"; export const IncompleteTodos = (props) => { const { todos, onClickComplete, onClickDelete } = props; return ( <div className="incomplete-area"> <p className="title">未完了のTODO</p> <ul> {todos.map((todo, index) => { return ( <div key={todo} className="list-row"> <li>{todo}</li> <button onClick={() => onClickComplete(index)}>完了</button> <button onClick={() => onClickDelete(index)}>削除</button> </div> ); })} </ul> </div> ); }; Input src/components/InputTodo.jsx import React from "react"; export const InputTodo = (props) => { const { todoText, onChange, onClick } = props; return ( <div className="input-area"> <input placeholder="TODOを入力" value={todoText} onChange={onChange} /> <button onClick={onClick}>追加</button> </div> ); }; 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React基礎文法

Reactについて勉強したのでその時のメモです。 JSX記法 JacaScript内にHTMLを書いていく感じ。 return内が複数行になるなら()で囲う。 JSXは一つのタブ<></>で囲う。 import React from "react"; import ReactDom from "react-dom"; const App = () => { return( <> <h1>こんにちは!</h1> <p>お元気ですか?</p> </> ); }; ReactDOM.render(<App />, document.getElementById("root")); コンポーネントの使い方 画面要素の1単位のことで1画面からテキストボックスなど大小様々。 ファイルを分けることでコンポーネント分割できる。 読み込むときはimport読み込ませるときはexportを使う。 Reactのコンポーネントを使用している場合は拡張子はjsxにするといい。 コンポーネント名は頭文字は大文字にする。 index.js import React from "react"; import ReactDom from "react-dom"; import { App } from "./App"; ReactDom.render(<App />, document.getElementById("root")); App.jsx import React from "react" export const App = () => { return( <> <h1>こんにちは!</h1> <p>お元気ですか?</p> </> ); }; Reactでのイベントとスタイル イベント イベント名はキャメルケース(onChange、onClickのような形のこと) {}で囲った部分がJavaScriptになり関数名などを入れる。 import React from "react" export const App = () => { const onClickButton = () => alert() return( <> <h1>こんにちは!</h1> <p>お元気ですか?</p> <button onClick={onClickButton}>ボタン</button> </> ); }; スタイル {}内でオブジェクトとしてスタイルを書く方法。その際割り当てる値は文字列にする。 ※ ① オブジェクトの変数の中にCSSを定義して反映する方法。 ※ ② スタイルの当て方は他にもある。 import React from "react" export const App = () => { const contentStyle = { // ② color: 'blue' fontSize: '18px' }; return( <> <h1 style={{ color: 'red' }}>こんにちは!</h1>  // ① <p style={contentStyle}>お元気ですか?</p> // ② </> ); }; Props コンポーネントに渡される引数のようなもの。 動的に変更したい部分をpropsとして使う。 変数名とchildrenで値を受け渡せる。 渡ってきたpropsに分割代入することで可読性UP。 例:スタイルで色のついた文字を表示させる場合 components/ColorfulMessage.jsx import React from "react"; export const ColorfulMessage = (props) => { const { color, children } = props; const contentStyle = { color: color }; return <p style={contentStyle}>{children}</p>; }; App.jsx import React from "react"; import { ColorfulMessage } from "./components/ColorfulMessage"; export const App = () => { return ( <> <ColorfulMessage color="green">お元気ですか?</ColorfulMessage> <ColorfulMessage color="red">こんにちは</ColorfulMessage> </> ); }; 結果 State コンポーネントが持つ状態のこと。 Stateが変更されると再レンダリングされる。 useState Reactでstateを扱う場合に使う関数。 const [stateの変数名, stateを変更する関数(setを使う) ] = useState(初期値) 使い方の例 カウントアップ stateで初期値を0に設定しボタンを押したらカウントアップする関数を定義することでカウントアップを実現できる。 import React from "react"; import { useState } from "react"; export const App = () => { const [num, setNum] = useState(0); const onClickCountUp = () => { setNum(num + 1); }; return ( <> <button onClick={onClickCountUp}>カウントアップ</button> <p>{num}</p> </> ); };  再レンダリング Reactではstateの変更があると再レンダリング(コードをまた上から実行)走る。 再レンダリングはバグの原因になることがある。 useEffect useEffectを使うことで再レンダリングを防止する。 useEffect(() => { // 処理 }, [] ); 第2引数が空だと処理の部分が最初にレンダリングされたときだけ実行される。 第2引数に変数を設定すればその変数を監視するのでその変数のstateが変わった時に実行される。 使う例としては、APIで記事一覧ページを取得したときにuseEffectで記事一覧ページを取得するのは初回のみと設定できたりなど。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】bladeファイルでReact側に変数を渡す

はじめに Laravelで一部分をReactでSPA化した際、APIを使うまでもなさそうな変数をグローバル変数として渡していました。 一応、備忘録として残します。 実装 script内でconst access_token = "{{ $access_token }}"のように値を渡します。 @section('main') <link href="{{ asset('/css/app.css') }}" rel="stylesheet" type="text/css"> <script> const access_token = "{{ $access_token }}" const api_url = "{{ url('/graphql') }}"; const app_store_url = "{{ env('APP_STORE_URL') }}" const pet_age = @json(config('define.pet.age')); </script> <div id="app"></div> <script src="{{asset('/js/app.js')}}"></script> @stop 以下のような連想配列(および配列)を渡す際には、@jsonでJSONに変換して渡します。 pet.php <?php return [ 'age' => [ 'dog' => 5, 'cat' => 10, 'bird' => 8, ] ]; React側で変数を呼び出す際、TypeScriptを使っているとWarningCan't find name ~がでるので、新しく変数を用意して// @ts-ignoreをつけてあげます。 index.ts const Component = () => { // @ts-ignore const petAge = pet_age ... } 参考資料
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactの特徴その1

Reactとは ReactとはFacebook社によって開発されたJavaScriptのためのユーザインターフェイス構築用のライブラリのこと。 SPA Single Page Applicationの略。 HTMLは1つのみでJavaScriptで画面を書き換える仕組み。ナウそうな奴は大体SPA。 最初に画面(HTML)を取得した後は、JavaScriptによって画面の書き換えを行うことで 単一ページでありながらアプリケーションを構築できる 必要な箇所だけ書き換える Reactでは仮想DOMとしてメモリ上にDOMの状態をキャッシュしておき、仮想DOMに差分が発生した場合にのみ差分を計算し、実際のDOMに差分のみを再描写することにより効率化している。 JSファイルだけどHTML風に書ける JSX(JavaScript XML)は、Reactのコンポーネント内でマークアップ言語を記述するためのXML風の拡張言語である。 jsファイルのreturn括弧内でHTMLタグが記述可能で、レンダリングによってDOMが構築される。このDOMは全体を1つのタグでまとめる必要があり、 フラグメント (fragment) を用いることで、DOM に余分なノードを追加することなく子要素をまとめることが出来る。フラグメントの宣言は <React.Fragment>~</React.Fragment> あるいは短縮記法で <>~</>で行う。 コードを分割する 1つのファイルに書いていたら膨大な量になってしまうのでコンポーネント(部品)化する。コンポーネントの名前は大文字で始めること!コンポーネントの拡張子は.jsのままでも使えるが、わかりやすく .jsxを使った方が良い。 以下は関数をコンポーネント化し、別ファイルで使用する例。 Comp.jsx //  エクスポート `export default モジュール名` import React from 'react' const App = () => { return ( <React.Fragment>  <h1>磁冠百柱林闘</h1> <p> 中国宋代、四川省拳法家達によって盛んに行われた異種格闘技。当時は杉木立の枝を払い、頂部を切断してその切り口を足場とした。 その杉は最低でも高さ一五米以上、直径は一〇センチから3米までと変化に富み、落下しようものなら即死は間違いなく、まさに命がけの勝負であった。 後に四川省青陽山で強磁石が発見されるにいたり、杉の頂部に鉄板をかぶせ磁靴を履き、二名対二名で戦う磁冠百柱林闘が完成した。 千変万化の中国格闘技にあってもこの磁冠百柱林闘は最高の技量とチームワークを要求される高度な戦いのひとつである。 現代のプロレスに見られるタッグマッチはこの磁冠百柱林闘を彷彿とさせる。 中津川大観著時源出版刊『偉大なる中国拳法』より</p> </React.Fragment> ); } export default App; index.js // インポート `import モジュール名 from 相対パス` import React from "react"; import ReactDom from "react-dom"; import App from "./Comp"; ReactDom.render(<App />, document.getElementById("root")); デフォルトエクスポートの場合、インポート側で任意に名前を設定することが出来る。 上記の例だと、import Foo from "./Comp";とすることで、新たに名前を付与することも可能。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む