【React】基礎のポイント振り返り
Reactの勉強記録。 こちらのトラハックさんのReact入門基礎編の振り返り
コンポーネント
見た目と機能を持つUI部品
大きく2つに分かれる
コンポーネントのいいところ
- 再利用するため
- 同じ記述を何度もする必要がない
- コードの見通しをよくするため
- 1コンポーネント=1ファイル
- 責務を明確にする(何のためのパーツなのか)
- 別ファイルに分ける
- 変更に強くするため
- 修正は1箇所
基本的な使い方
//App.jsx(親) import Article from "./components/Article"; function App() { return ( <div> <Article /> </div> ) } export default App;
// components/Article.jsx(子) const Article = () => { // アロー関数バージョン return <h2>こんにちは</h2> }; export default Article;
propsでデータを受け渡す
//App.jsx(親) import Article from "./components/Article"; function App() { return ( <div> <Article title={'勉強中'} content={'propsについて'} /> </div> ) } export default App;
// components/Article.jsx(子) const Article = () => { return ( <div> <h2>{props.title}</h2> <p>{props.content}</p> </div> ) }; export default Article;
- 子コンポーネントの引数に
props
を指定する - 親から子にデータを渡す
#コンポーネントのimportとexport
JavaScriptのモジュール機能
- プログラムをモジュールという単位に分割する
- 原則、1ファイル=1モジュール
- 必要なときに必要なモジュールのみ読み込む
default export(名前なしexport)
// アロー関数 const Title = (props) => { return <h2>{props.title}</h2> }; export default Title;
// 名前付き関数 export default function Title(props) { return <h2>{props.title}</h2> };
- 推奨されるexport方法
- 1ファイル=1export
- 一度宣言したアロー関数をdefault export
- 名前付き関数宣言と同時にdefault export
default import(名前なしimport)
// Article.jsx(export元) const Article = (props) => { return ( <div> <h2>{props.title}</h2> <p>{props.content}</p> </div> ); }; export default Article;
// App.jsx(import先) import Article from "./components/Article"; function App() { return ( <Article title={'タイトル'} content={'中身'} /> ); };
- default exportしたモジュールをそのまま読み込む
名前付きexport
// helper.js(1ファイルに便利関数が複数入っている) export const addTax = (price) => { return Math.floor(price * 1.1) } export const getWild = () => { console.log('Get wild and touch') } // index.js export {default as Article} from './Article' export {default as Content} from './Content' export {default as Title} from './Title' // 別名(エイリアス)はなんでもいいけどファイル名にするのが一般的
- 1ファイルから複数モジュールをexportしたいとき
- Reactではエントリポイントでよく使う
- エントリポイントでは別名exportも併用する
名前付きimport
// 直接コンポーネントを読み込むのではなく、エントリポイント(index.jsx)を通してimport import {Content, Title} from "./index.jsx"; const Article = (props) => { return ( <div> <Title title={props.title} /> <Content content={props.content} /> </div> ); }; export default Article;
- 1ファイルから複数モジュールを読み込む
- エントリポイントから複数コンポーネントを読み込む
コンポーネントの状態管理
Hooks
なぜstateを使うのか
- Reactコンポーネント内の値を書き換えたい
- ❌:DOMで直接書き換える
- ⭕️:新しい値を使って再レンダリングさせる
- Reactコンポーネントが再レンダリングするきっかけは?
- stateが変更されたとき
- propsが変更されたとき
- 子から親にpropsを渡すことはできない
- 親で更新用の関数を用意しておいて、子でその更新用の関数を実行して、親のpropsを変更させる
useStateの使い方
- userStatueによるstateの宣言
const [state, setState] = useState(initialState) 現在の状態 更新関数 初期値
- stateの更新
setState(newState) 更新関数 新しい値
- 具体例
const [message, setMessage] = useState('test!!') const [links, setLinkes] = useState(0) const [isPublished, setIsPublished] = useState(false) setIsPublished(true)
stateをpropsに渡す
// Article.jsx const Article = (props) => { const [isPublished, setIsPublished] = useState(false) const publishArticle = () => { setIsPublished(true) } return ( <div> <Title title={props.title} /> <Content content={props.content} /> <PublishButton isPublished={isPublished} onClick={publishArticle} /> </div> ); };
// PublishButton.jsx const PublishButton = (props) => { return ( <button onClick={() => props.onClick()}> 公開状態: {props.isPublished.toString()} </button> ); } export default PublishButton;
- 更新関数はそのままpropsとして渡さず関数化する
- 関数をpropsに渡す時は注意する
propsへ関数を渡す際の注意点
- OKな関数の渡し方
- コールバック関数化関数自体を渡す
<PublishButton isPublished={isPublished} onClick={publishArticle} /> // コールバック関数 <PublishButton isPublished={isPublished} onClick={() => publishArticle()} />
- NGな関数の渡し方(無限レンダリングが起こる)
- propsに渡すときに関数を実行しない
// ()をつけて関数を実行してしまっている <PublishButton isPublished={isPublished} onClick={publishArticle()} />
頻出するuseStateの使い方
引数を使って更新する
import React, {useState} from 'react'; const TextInput = () => { const [name, setName] = useState('') const handleName = (event) => { setName(event.target.value) } return ( <input onChange={(event) => handelName(event)} // eventはonChangeイベントの戻り値 type={'text'} value={name} /> ); }; export default TextInput;
- 入力フォームでよく使う
- onChangeイベントでhandleName関数に渡す
- handelName関数のパラメータであるeventを更新関数に渡す
prevStateを活用する
import React, {useState} from 'react'; const Counter = () => { const [count, setCount] = useState(0) const countUp = () => { setCount(prevState => prevState + 1) } const countDown = () => { setCount(prevState => prevState - 1) } return ( <div> <p>現在のカウント数: {count}</p> <button onClick={countUp}>up</button> <button onClick={countDown}>down</button> </div> ); }; export default Counter;
- useStateの更新関数で使える特殊はprevState
- prevStateは更新前のstate
- prevStateに変更を加えてreturn
こっちの書き方は良くない!
import React, {useState} from 'react'; const Counter = () => { const [count, setCount] = useState(0) const countUp = () => { setCount(count + 1) } const countDown = () => { setCount(count - 1) } return ( <div> <p>現在のカウント数: {count}</p> <button onClick={countUp}>up</button> <button onClick={countDown}>down</button> </div> ); }; export default Counter;
- countを直接書き換えている
- 更新をしますとしてから、実際に更新されるまで多少のタイムラグが起こり、正しく変更されない(非同期で変更するため、実際に更新されるまでは前回の状態を参照している)
ON/OFFを切り替えるボタン
import React, {useState} from 'react'; const ToggleButton = () => { const [open, setOpen] = useState(false) const toggle = () => { setOpen(prevState => !prevState) } return ( <button onClieck={toggle}> {open ? 'OPEN' : 'CLOSE'} </button> ); }; export default ToggleButton;
- prevStateで受け取った値を ! で反転してreturnする
- 三項演算子によってopenがtrue/falseで表示を切り替える
ライフサイクルと副作用(useEffect)
ライフサイクルとは
- コンポーネントが生まれてから破棄されるまでの時間の流れ
- ライフサイクルメソッドを使うと、時点に応じた処理を実行できる
- Class Component時代は以下の3メソッドが頻出だった
- componentDidMout()
- componentDidUpdate()
- componentWillUnmount()
- Hooks時代はuseEffectでライフサイクルを表現
3種類のライフサイクル
副作用(effect)フックを使おう
constCounter = () => { const [count, setCount] = useState(0) const countUp = () => { setCount(prevState => prevState + 1) } const countDown = () => { setCount(prevState => prevState - 1) } // countというstateが変更されるたびに再レンダリング起きて、その度に起こされる処理 useEffect(() => { console.log("Current count is...", count) }) return ( <div> <p>現在のカウント数: {count}</p> <button onClick={countUp}>up</button> <button onClick={countDown}>down</button> </div> ); };
第二引数の依存関係を理解する
// 毎回実行される useEffect(() => { console.log("Current count is...", count) }) // 初回レンダリング後のみ実行される useEffect(() => { console.log("Current count is...", count) }, []) // triggerが変更されるたびに実行される useEffect(() => { console.log("Current count is...", count) }, [trigger]) // trigger1かtrigger2が変更されるたびに実行される useEffect(() => { console.log("Current count is...", count) }, [trigger1, trigger2])
- useEffectの第二引数には配列を渡すことが可能
- 第二引数はdeps(dependencies)と呼ばれ、副作用が引き起こされるどうかの依存関係となる
クリーンアップを理解する
const ToggleButton = () => { const [open, setOpen] = useState(false) const toggle = () => { setOpen(prevState => !prevState) } useEffect(() => { console.log("Current state is", open) if (open) { console.log("Subscribe database...") } return () => { console.log("Unsbscribe database!") } }) return ( <button onClieck={toggle}> {open ? 'OPEN' : 'CLOSE'} </button> ); };
- コンポーネントないで外部データベースを購読したい
- useEffectないで購読処理を呼び出す
- 必要無くなったらクリーンアップ関数を使って掃除する
- useEffectの中でreturnする
useEffect内でAPIを呼び出そう
useEffectのユースケース
- APIやデータベースから非同期通信でデータを取得(fetch)する
- 特定の値が変わったらデータを再取得(refetch)する
useEffect内で非同期通信
useEffect(() => { fetch(`https://api.github.com/users/${id}`) .then(res => res.json()) .then(data => { console.log(data) setName(data.name) }) .catch(error => { console.error(error) }) }, [id])
- 初回レンダリング後に呼び出される
- 第二引数に指定した値が変わるたびに再度呼び出される
- 取得した値をuseStateの更新関数に渡す