20211022のReactに関する記事は2件です。

【React】関数コンポーネントでのデータ修正処理は分割代入を使え

Reactもだいぶ、バージョン16.8以降、すなわち関数コンポーネントでの記述方法が主流となってきた気がします。そして、この関数コンポーネントの記法になってから、データ処理、同期に際してhooks(useStateなど)と同時に重要な要素として、採り上げておくべき事項があると感じました。 それは、分割代入を用いないと能率的なデータ更新、同期が取れないということです。特に、データ修正に際して、まだあまり方法を把握されていないReact初心者を多く感じたので、記事にしてみました。 買い物かごを作ってみる では、演習用のプログラムとして単純な買い物かごシステムを作ってみます。仕様は以下の通りです。 商品群productsに対し、商品をリスト化する。 買い物かごに追加ボタンをクリックすると、買い物かごリストに追加される。 同じ商品が追加された場合、買い物かごリストの個数が加算される。 買い物かごリストから取消ボタンがクリックされた場合、商品を買い物かごリストから抹消する。 それで作ってみた演習用プログラムが以下の通りです。 参考ソースはこちら Build a React Hooks Shopping Cart with useState and useEffect ※Googleでshopping cart react hooksで検索すると色々出てくるので、参考になります。 import React, { useState} from "react" const ShoppingCart = ()=>{ const [cart, setCart] = useState([]) const products = [ { id: 1, name: "SHURE SRH1840", price: 59800, }, { id: 2, name: "Zenhizer HD650", price: 46000, }, { id: 3, name: "HIFIMAN SUNDARA", price: 38000, }, ] const total = cart.reduce((total,{price = 0,unit})=> total + price * unit,0) const addToCart = (el)=>{ const updCart = [...cart] let sameindex = updCart.findIndex((item)=> item.id === el.id) if(sameindex >= 0 ){ const edit = {...updCart[sameindex]} edit.unit++ updCart[sameindex] = edit }else{ updCart.push({...el,unit: 1}) } setCart(updCart) } const removeFromCart = (el)=>{ let hardCopy = [...cart] hardCopy = hardCopy.filter((cartItem)=> cartItem.id !== el.id) setCart(hardCopy) } const productItems = products.map((el)=>( <div key={el.id}> {`${el.name}:${el.price}円`} <input type="submit" value="買い物かごへ" onClick={()=> addToCart(el)} /> </div> )) const cartItems = cart.map((el)=>( <div key={el.id}> {`${el.name}: ${el.price}円: 個数 ${el.unit}`} <input type="submit" value="取消" onClick={()=> removeFromCart(el)} /> </div> )) return( <div> STORE <div>{productItems}</div> <div>買い物かごリスト</div> <hr/> <div>{cartItems}</div> <div>総額: {total}</div> </div> ) } export default ShoppingCart データ更新処理の詳細説明 ここがこの記事で特別採り上げておきたいデータの更新部分で、分割代入を効率的に用いることで、ここまでスムーズに記述できます。 const updCart = [...cart] //買い物かごcartを分割代入し、同期用のオブジェクトupdCartを作成 let sameindex = updCart.findIndex(item => item.id === el.id) //既存商品のインデックス抽出 if(sameindex >= 0 ){ const edit = {...updCart[sameindex]} //既存商品の抽出 edit.unit++ //個数の加算 updCart[sameindex] = edit //値の更新 }else{ updCart.push({...el,unit: 1}) //選択商品の新規挿入 } setCart(updCart) //同期処理 データの更新と同期 既存のデータを更新する場合はまず、対象商品のインデックスを取得するために let sameindex = updCart.findIndex(item => item.id === el.id) このように記述していますが、これは買い物かごcartを分割代入した更新用オブジェクトupdCart(現時点ではcartと同じ値、すなわち初期動作だと中身は[]となっている)に対し、item.idとel.id(選択した商品のid)が一致しているインデックスを取得してきます。初期動作だと何も入っていないため、この値は-1を返してくれるので、新規挿入に分岐が振り分けられます。 さて、同じ商品をもう一度押した場合、更新処理が行われることになります。それが以下の部分です。まずは対象商品のインデックス番号が取得できたので、それに該当する商品を分割代入して編集用変数editに代入します。このeditの値のみ変更可能となるので、ここで値のインクリメント処理を行います。そして、もう一度updCart[sameindex]に代入することで、このupdCart内のプロパティunitは2を示します。 const edit = {...updCart[sameindex]} //既存商品の抽出 edit.unit++ //値のインクリメント updCart[sameindex] = edit //値の更新 ただ、これだとまだ同期が取れていないのであとはsetCart(updCart)として同期を取りましょう。これでリアルタイムなオブジェクトの値の修正がスムーズに行なえます。 配列をmapで回す場合 配列の中からmapで回す方法もあります。テーブル情報の変更やステータス情報の切替など、既存のデータが必ず存在し、その値を変更したりする場合ならこの方法でもいいでしょう。 このようなサンプルでクリックしたリストのステータスが変化します。 import React, { useState,useEffect } from "react"; const PushBox = ()=>{ const [vals,setVal] = useState([]) const items = [ {"id":1,"name":"札幌"}, {"id":2,"name":"仙台"}, {"id":3,"name":"東京"}, {"id":4,"name":"名古屋"}, {"id":5,"name":"大阪"}, {"id":6,"name":"広島"}, {"id":7,"name":"福岡"}, ] useEffect(()=>{ setVal(items) },[]) const upd = (sel)=>{ items.map((item,idx)=>{ if(item.id == sel){ const edit = items item.name = item.name+"を選択!" edit[idx] = item   setVal(edit) } }) } return( <ul> {vals.map((item,idx)=>( <li key={item.id}> {item.name} <input type="button" onClick={ ()=> upd(item.id)} value="push" /> </li> ))} </ul> ) } export default PushBox } 毎回、選択された都市が入れ替わるのはitemsに随時代入しているからですが、再度別のボタンをクリックすると、リストitemsは初期情報が保持されるので入れ替わります。もし、選択した値を保持したい場合は const edit = [...vals] //常に同期をとっておきたいリストを退避用の変数に分割代入する item.name = item.name+"を選択" //対象の値を更新する。map関数内の引数から edit[idx] = item //更新情報を編集用の変数に代入 setVal(edit) //同期処理。必ずmap関数内で行うこと! このようにすれば、移し替えたリスト情報の分割代入を行っているので、リスト情報を保持することができます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

さくっと始めるRecoil入門

はじめに 今回はReactの状態管理ライブラリであるRecoilについてまとめていきます。 それでは、頑張って行きましょう。 Recoilとは RecoilはFacebook社が作成した状態管理ライブラリです。Context APIの成約を解決するために開発されたみたいです。 以下、こちらの記事の引用 Context APIの制約 Reactのみで状態管理を完結させようとすると、Context APIを用いてアプリケーションの状態を管理させることになります。基本的に問題はないのですが、Context APIにはいくつかの制約があります。 たとえばContext APIで管理されているReactコンポーネントの状態を変更するためには祖先コンポーネントまで辿ってツリーを更新しなくてはいけません。大元のコンポーネントが巨大だった場合はそのぶん更新するツリーも大きくなるため、再レンダリングのコントロール難易度が上がってしまいます。うまく管理しないと巨大なツリー更新によるパフォーマンスの低下を招いたり、コンポーネントの状態が意図せずリセットされてしまうことが起こりかねません。 Recoilとは Recoilは、Context APIが抱えるこれらの制約・問題を解決するためにFacebookによって提唱された実験的な状態管理ライブラリです。「Atom」「Selector」と呼ばれる単位を使用してアプリケーションデータを管理し、各Atomには一意のキーとそれが管理するデータの一部が含まれています。各Selectorは複数のAtomにもとづく派生状態の一部で、Atom・他のSelectorを受け取る純粋な関数として定義します。これらの単位をHooks APIで操作しながら状態管理を行うのがRecoilです。 Recoilのざっくりとした使い方 それでは、Recoilのざっくりとした使い方を見ていきましょう。 Recoilの導入 まずは、以下のコードでRecoilをインストールしましょう。 npm install recoil あとは、_app.tsxを<RecoilRoot>で囲うだけです。 app.tsx import { RecoilRoot } from 'recoil'; export default function MyApp(props: AppProps): JSX.Element { const { Component, pageProps } = props; return ( <RecoilRoot> <Component {...pageProps} /> </RecoilRoot> ); } Atomの作成 RecoilはAtomというステートオブジェクトを作成して使用します。Atomは、Stateとセッター関数を切り出すことができる箱のような概念であり、このAtomをエクスポート・インポートすることによって、コンポーネントの間でStateを共有します。 また、AtomからStateやセッター関数を切り出すときは、hooksのインターフェースを用いるため、学習コストも低めです。 AtomからStateのみを切り出しても良いし、セッター関数のみを切り出しても構いません。 まずは、Atomを作成してみましょう const countState = atom<number>({ key: 'sample/count', default: 0 }); AtomからStateとセッター関数の切り出し それでは次に、このAtomからStateとセッター関数を切り出しましょう。 import { useRecoilState } from 'recoil'; const Counter = () => { // atomから状態を取り出す const [count, setCount] = useRecoilState(countState); return <div onClick={() => setCount((c) => c + 1)}>Clicked: {count}</div>; }; AtomからuseRecoilStateを用いてStateとセッター関数を切り出すのは、useStateを用いてStateとセッター関数を作成する感覚とほぼ同じです。 作成したAtomをエクスポートし、使用したいコンポーネントでインポートすれば、状態をコンポーネント間で共有することができます。 AtomからStateのみ、セッター関数のみを切り出し useRecoilValueを使用すると、AtomからStateだけを切り出す事ができます。 import { useRecoilValue } from 'recoil'; const Counter = () => { // atomから状態を取り出す const count = useRecoilValue(countState); return <div>{count}</div>; }; また、useSetRecoilStateを使用すると、セッター関数のみを切り出すことができます。 import { useSetRecoilState } from 'recoil'; const Counter = () => { // atomからセッター関数を取り出す const setCount = useSetRecoilState(countState); return <div onClick={() => setCount((c) => c + 1)}>Increment!</div>; }; ここまでが、Recoilのざっくりとした使い方になります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む