React 復習

JSX

// { } でJavaScriptの記法と認識される
<button onClick={}>ボタン</button>
// 外側の{ } はJavaScriptの記法と認識される
// 内側の{ } はJavaScriptのオブジェクトのこと
<h1 style={{ color: 'red' }}>こんにちは!</h1>

イベント発火

export const App = () => {
  const onClickButton = () => alert();

  return (
    <div>
      <button onClick={onClickButton}>ボタン</button>
    </div>
  )
}

Props

コンポーネントに対して渡す引数のようなもの

import { HelloWorldMessage } from "./components/HelloWorldMessage";

// コンポーネントに対して値を渡す
<HelloWorldMessage message="hello" />
<HelloWorldMessage message="world" />
// propsを受け取る(helloとworldが表示される)
export const HelloWorldMessage = (props) => {

  return (
    <p>{props.message}</p>
  );
}

children

タグで囲った値をchildrenとして渡すこともできる

import { HelloWorldMessage } from "./components/HelloWorldMessage";

// コンポーネントに対して値を渡す
<HelloWorldMessage>
  hello
</HelloWorldMessage>
<HelloWorldMessage>
  world
</HelloWorldMessage>
// propsを受け取る(helloとworldが表示される)
export const HelloWorldMessage = (props) => {

  return (
    <p>{props.children}</p>
  );
}

分割代入

分割代入で毎回propsを指定しなくてもよくなる

export const HelloWorldMessage = (props) => {
  const { color, children } = props;

  const contentStyle = {
    color: color,
    // ↑オブジェクトのプロパティ名と当てはめる値が同じ時は省略できる↓
    color,
    fontSize: '18px',
  };

  return (
    <p style={contentStyle}>{children}</p>
  );
}

State

コンポーネントが持つ状態(可変)

useState

import { useState } from "react";

// 第一引数:stateとして使用する変数名
// 第二引数:stateを変更するた関数名
// useState('初期値')を設定できる
const [num, setNum] useState(0);

<p>{num}</p> // 0

ボタンをクリックするとnumの値が1ずつ増えるイベント

import { useState } from "react";

export const App = () => {
  const [num, setNum] = useState(0);

  const onClickCountUp = () => {
    setNum((prevNum) => prevNum + 1);
  };

  return (
    <div>
      <button onClick={onClickCountUp}>カウントアップ</button>
      <p>{num}</p>
    </div>
  );
};

※js

// アロー関数はreturnで返す行が一行の時は省略できる
const a = (str) => {
  return str
}

// ↓
const a = (str) => str;

// ↓( ) も省略することもできる
const a = str => str;

// && 左辺がtrueの時に右辺を返す、左辺がfalseの時は右辺を返さない
{faceShowFlag && <p>orz</p>}

// || は左辺がfalseなら右辺を返す、左辺がtrueの時は右辺を返さない
{faceShowFlag || <p>orz</p>}

useEffect

Stateの更新があった時に再描画される(set関数が呼ばれてstateが更新された時に関数コンポーネントが頭から読み込まれる)
このような特定の条件の時にコンポーネントを再レンダリングして差分を反映することで
画面遷移を表現している

●再レンダリングする条件

・stateを変更した時
・コンポーネントのpropsの中身が変更した時
・親のコンポーネントが再レンダリングされた時(子コンポーネントも追随していく)
// useEffectの引数に空の配列[]を設定するとコンポーネント内で最初の1回だけ通したい処理を実行することができる
useEffect(() => {
    console.log('useEffect');
  }, []);
// 引数にnumを設定した場合、このuseEffectはnumにだけ関心を持つようになる
// そのため今回であればnumのstateが変更した時だけuseEffectが通るようになる
useEffect(() => {
    console.log('useEffect');
  }, [num]);
const [faceShowFlag, setFaceShowFlag] = useState(false);

useEffect(() => {
    if (num % 3 === 0) {
      // faceShowFlagがfalseの時だけset関数を呼び出しtrueに変更する
      faceShowFlag || setFaceShowFlag(true);
    } else {
      // faceShowFlagがtrueの時だけset関数を呼び出しfalseに変更する
      faceShowFlag && setFaceShowFlag(false);
    }
  }, [num]);

●行うこと
・ボタン(on/off)をクリックすると顔文字を表示・非表示にする
・3の倍数の時だけ顔文字を表示する

/*eslint react-hooks/exhaustive-deps: off */
import { useEffect, useState } from "react";

export const App = () => {
  const [num, setNum] = useState(0);
  const [faceShowFlag, setFaceShowFlag] = useState(false);

  const onClickCountUp = () => {
    setNum((prevNum) => prevNum + 1);
  };

  const onClickSwitchShowFlag = () => setFaceShowFlag(!faceShowFlag);

  //  「Too many re-renders」が発生するため引数にnumを指定し、このuseEffectはnumにだけ関心を持つように設定する
  useEffect(() => {
    if (num <= 0) {
      return
    }
    if (num % 3 === 0) {
      // faceShowFlagがfalseの時だけset関数を呼び出しtrueに変更する
      faceShowFlag || setFaceShowFlag(true);
    } else {
      // faceShowFlagがtrueの時だけset関数を呼び出しfalseに変更する
      faceShowFlag && setFaceShowFlag(false);
    }
  }, [num]);

  return (
    <div>
      <button onClick={onClickCountUp}>カウントアップ</button>
      <br/>
      <button onClick={onClickSwitchShowFlag}>on/off</button>
      <p>{num}</p>
      {faceShowFlag && <p>orz</p>}
    </div>
  );
};

Todo

import { useState } from "react";
import "./styles.css";

export const App = () => {
  const [incompleteTodos, setIncompleteTodos] = useState(['テスト1', 'テスト2']);
  const [completeTodos, setCompleteTodos] = useState(['テストend']);

  return (
    <div>
      <div className="input-area">
        <input placeholder="TODOを入力" />
        <button>送信</button>
      </div>
      <dir 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>
            )
          })}
        </ul>
      </dir>
      <dir className="complete-area">
        <p className="title">完了したTODO</p>
        <ul>
          <div className="list-row">
            <li>テストend</li>
            <button>戻す</button>
          </div>
        </ul>
      </dir>
    </div>
  );
};
const [incompleteTodos, setIncompleteTodos] = useState(['テスト1', 'テスト2']);

// ループ処理させる場合、ループ内で返却している一番親タグにkeyの指定が必要
// 変更前と変更後で差分だけ抽出しその差分のみ実際のDOMに反映していくため、ループでレンダリングされた場合、何個目の要素なのかを正確に比較するために目印をつける必要がある
{incompleteTodos.map((todo) => {
  return (
    <div key={todo} className="list-row">
      <li>{todo}</li>
      <button>完了</button>
      <button>削除</button>
    </div>
  );
})}

inputタグに入力した値を取得・追加

const [todoText, setTodoText] = useState('');

// 引数にイベントを受け取る
// e.target.valueで入力した値を取得しsetTodoText関数で更新する
const onChangeTodoText = (e) => setTodoText(e.target.value);

  const onClickAdd = () => {
    if (todoText === '') return alert('TODOを入力してください');

    // スプレッド構文で現在のincompleteTodosの中身を複製しtodoTextで新しい配列を生成(newTodos)
    // 新しく生成したnewTodosをsetIncompleteTodos関数で更新
    const newTodos = [...incompleteTodos, todoText];
    setIncompleteTodos(newTodos);
    setTodoText('');
  };

// onChangeでinputの値に変更があれば検知する(onChangeがないと初期値の空文字が設定され続ける)
<input placeholder="TODOを入力" value={todoText} onChange={onChangeTodoText} />
<button onClick={onClickAdd}>送信</button>

関数に引数を渡したい場合

// アロー関数を使い新しく関数を生成する必要がある
<button onClick={() => onClickDelete(index)}>削除</button>

削除

const onClickDelete = (index) => {
  const newTodos = [...incompleteTodos];
  // splice() 第一引数に何番目の要素か、第二引数にいくつ削除するか指定する
  newTodos.splice(index, 1);
  // このnewTodosでsetIncompleteTodosを更新する
  setIncompleteTodos(newTodos);
}

<ul>
  {incompleteTodos.map((todo, index) => {
    return (
      <div key={todo} className="list-row">
        <li>{todo}</li>
        <button>完了</button>
        <button onClick={() => onClickDelete(index)}>削除</button>
      </div>
    );
  })}
</ul>