React 覚え書き

# プロジェクト作成

```sh
npx create-react-app react-sample
cd react-sample
npm start または yarn start
```

# コメント

- jsxの中では、`{/* コメント */}` を使う。
- タグの中は、`/* */` `//` も使える

# クラスコンポーネント

```jsx:src/App.js
import React, { Component } from "react";

class App extends Component {
  render() {
    return <p>Hello world {this.props.name}!</p>;
  }
}
export default App;
```

# 関数コンポーネント

- シンプルだが、State、ライフサイクルメソッドが使えない
- Hookを使うことにより、上記問題は解決する

```jsx
import React from "react";

const App = (props) => {
  return <p>Hello world {props.name}!</p>;
};
export default App;
```

# props

- `<App name="ichiro"></App>` の場合 `{this.props.name}` = `ichiro`
- `<App><p>こんにちは</p></App>` の場合 `{this.props.children}` = `<p>こんにちは</p>`


# 繰り返し

```jsx
import React from "react";

const App = () => {
  const users = [{ name: "yamada" }, { name: "suziki" }, { name: "saito" }];

  return (
    <dl>
      {users.map((user, index) => (
        <ul key={index}>{user.name}</ul>
      ))}
    </dl>
  );
};
export default App;
```

# 条件分岐

```jsx
// 条件演算子
{ this.props.isNew ? <NewIcon /> : null }
// &&演算子
{ this.props.isNew && <NewIcon /> }
// 即時関数
{(() => {
  if (this.props.isNew) {
    return <NewIcon />
  }
})}
```

# stateを使ったフォーム

- constructor()の中で、stateの初期化を行う。
- this.setState()でstateの更新を行う。

```jsx
import React, { Component } from "react";

export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: "",
      prefecture: "",
      career: "",
      skill: [""],
      memo: "",
    };
    this.handleChange = this.handleChange.bind(this);
    this.handleChangeMulti = this.handleChangeMulti.bind(this);
    this.file = React.createRef(); // fileを参照できるようにする(Uncontrolled Component)
    this.show = this.show.bind(this);
  }

  handleChange(e) {
    this.setState({
      [e.target.name]: e.target.value,
    });
  }

  handleChangeMulti(e) {
    const fs = this.state.skill;
    if (e.target.checked) {
      fs.push(e.target.value);
    } else {
      fs.splice(fs.indexOf(e.target.value), 1);
    }
    this.setState({
      [e.target.name]: fs,
    });
  }

  show() {
    console.log(`${this.state.name}`);
    console.log(`${this.state.prefecture}`);
    console.log(`${this.state.career}`);
    console.log(`${this.state.skill}`);
    console.log(`${this.state.memo}`);

    const f = this.file.current.files[0];
    if (f) {
      console.log(`${f.name} ${f.type}  ${f.size}`);
    }
  }

  render() {
    return (
      <form>
        <label htmlFor="name">名前：</label>
        <input id="name" name="name" type="text" value={this.state.name} onChange={this.handleChange}></input>
        <br />

        <label htmlFor="prefecture">住所：</label>
        <select id="prefecture" name="prefecture" value={this.state.prefecture} onChange={this.handleChange}>
          <option value=""></option>
          <option value="tokyo">東京都</option>
          <option value="kanagawa">神奈川県</option>
          <option value="saitama">埼玉県</option>
          <option value="chiba">千葉県</option>
          <option value="ibaraki">茨城県</option>
          <option value="gunma">群馬県</option>
          <option value="tochigi">栃木県</option>
        </select>

        <fieldset>
          <legend>経験年数：</legend>
          <input id="career_zero" name="career" type="radio" value="zero" checked={this.state.career === "zero"} onChange={this.handleChange} />
          <label htmlFor="career_zero">未経験</label>
          <input id="career_one" name="career" type="radio" value="one" checked={this.state.career === "one"} onChange={this.handleChange} />
          <label htmlFor="career_one">1年未満</label>
          <input id="career_three" name="career" type="radio" value="three" checked={this.state.career === "three"} onChange={this.handleChange} />
          <label htmlFor="career_three">1～3年</label>
          <input id="career_five" name="career" type="radio" value="five" checked={this.state.career === "five"} onChange={this.handleChange} />
          <label htmlFor="career_five">3年～5年</label>
          <input id="career_over" name="career" type="radio" value="over" checked={this.state.career === "over"} onChange={this.handleChange} />
          <label htmlFor="career_over">5年以上</label>
        </fieldset>

        <fieldset>
          <legend>スキル：</legend>
          <input id="skill_java" name="skill" type="checkbox" value="java" checked={this.state.skill.includes("java")} onChange={this.handleChangeMulti} />
          <label htmlFor="skill_java">Java</label>
          <input id="skill_php" name="skill" type="checkbox" value="php" checked={this.state.skill.includes("php")} onChange={this.handleChangeMulti} />
          <label htmlFor="skill_php">PHP</label>
          <input id="skill_javascript" name="skill" type="checkbox" value="javascript" checked={this.state.skill.includes("javascript")} onChange={this.handleChangeMulti} />
          <label htmlFor="skill_javascript">Javascript</label>
          <input id="skill_python" name="skill" type="checkbox" value="python" checked={this.state.skill.includes("python")} onChange={this.handleChangeMulti} />
          <label htmlFor="skill_python">Python</label>
          <input id="skill_ruby" name="skill" type="checkbox" value="ruby" checked={this.state.skill.includes("ruby")} onChange={this.handleChangeMulti} />
          <label htmlFor="skill_ruby">Ruby</label>
        </fieldset>

        <br />
        <label htmlFor="memo">メモ：</label>
        <textarea id="memo" name="memo" value={this.state.memo} onChange={this.handleChange}></textarea>
        <br />

        <label htmlFor="file">ファイル：</label>
        <input id="file" name="file" type="file" ref={this.file}></input>
        <br />

        <button type="button" onClick={this.show}>
          送信
        </button>
      </form>
    );
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88686/f1001bc9-bd6a-9656-7fa2-f201cfc1eff2.png)


# useStateを使ったフォーム

```jsx
import React, { useState } from "react";

const App = () => {
  const [name, setName] = useState("");
  const [prefecture, setPrefecture] = useState("");
  const [career, setCareer] = useState("");
  const [skill, setSkill] = useState([]);
  const [memo, setMemo] = useState("");

  const file = React.createRef();

  const handleChange = (event) => {
    switch (event.target.name) {
      case "name":
        setName(event.target.value);
        break;
      case "prefecture":
        setPrefecture(event.target.value);
        break;
      case "career":
        setCareer(event.target.value);
        break;
      case "memo":
        setMemo(event.target.value);
        break;
      default:
        console.log("key not found");
    }
  };

  const handleChangeMulti = (e) => {
    if (skill.includes(e.target.value)) {
      setSkill(skill.filter((item) => item !== e.target.value));
    } else {
      setSkill([...skill, e.target.value]);
    }
  };

  const show = () => {
    console.log(name);
    console.log(prefecture);
    console.log(career);
    console.log(skill);
    console.log(memo);

    const f = file.current.files[0];
    if (f) {
      console.log(`${f.name} ${f.type}  ${f.size}`);
    }
  };

  return (
    <form>
      <label htmlFor="name">名前：</label>
      <input id="name" name="name" type="text" value={name} onChange={handleChange}></input>
      <br />

      <label htmlFor="prefecture">住所：</label>
      <select id="prefecture" name="prefecture" value={prefecture} onChange={handleChange}>
        <option value=""></option>
        <option value="tokyo">東京都</option>
        <option value="kanagawa">神奈川県</option>
        <option value="saitama">埼玉県</option>
        <option value="chiba">千葉県</option>
        <option value="ibaraki">茨城県</option>
        <option value="gunma">群馬県</option>
        <option value="tochigi">栃木県</option>
      </select>

      <fieldset>
        <legend>経験年数：</legend>
        <input id="career_zero" name="career" type="radio" value="zero" checked={career === "zero"} onChange={handleChange} />
        <label htmlFor="career_zero">未経験</label>
        <input id="career_one" name="career" type="radio" value="one" checked={career === "one"} onChange={handleChange} />
        <label htmlFor="career_one">1年未満</label>
        <input id="career_three" name="career" type="radio" value="three" checked={career === "three"} onChange={handleChange} />
        <label htmlFor="career_three">1～3年</label>
        <input id="career_five" name="career" type="radio" value="five" checked={career === "five"} onChange={handleChange} />
        <label htmlFor="career_five">3年～5年</label>
        <input id="career_over" name="career" type="radio" value="over" checked={career === "over"} onChange={handleChange} />
        <label htmlFor="career_over">5年以上</label>
      </fieldset>

      <fieldset>
        <legend>スキル：</legend>
        <input id="skill_java" name="skill" type="checkbox" value="java" checked={skill.includes("java")} onChange={handleChangeMulti} />
        <label htmlFor="skill_java">Java</label>
        <input id="skill_php" name="skill" type="checkbox" value="php" checked={skill.includes("php")} onChange={handleChangeMulti} />
        <label htmlFor="skill_php">PHP</label>
        <input id="skill_javascript" name="skill" type="checkbox" value="javascript" checked={skill.includes("javascript")} onChange={handleChangeMulti} />
        <label htmlFor="skill_javascript">Javascript</label>
        <input id="skill_python" name="skill" type="checkbox" value="python" checked={skill.includes("python")} onChange={handleChangeMulti} />
        <label htmlFor="skill_python">Python</label>
        <input id="skill_ruby" name="skill" type="checkbox" value="ruby" checked={skill.includes("ruby")} onChange={handleChangeMulti} />
        <label htmlFor="skill_ruby">Ruby</label>
      </fieldset>

      <br />
      <label htmlFor="memo">メモ：</label>
      <textarea id="memo" name="memo" value={memo} onChange={handleChange}></textarea>
      <br />

      <label htmlFor="file">ファイル：</label>
      <input id="file" name="file" type="file" ref={file}></input>
      <br />

      <button type="button" onClick={show}>
        送信
      </button>
    </form>
  );
};
export default App;
```

# ライフサイクルメソッド
|                        | Mounting | Updating | Unmounting |用途|
| :--------------------: | :------: | :-------: | :--------: | :--------: |
|   constructor(props)   |  初期化  |           |            |stateの初期化、thisの固定など|
|        render()        |  描画時  |  描画時   |            |必須。React要素、文字列値、数値、など|
|  componentDidMount()   |  配置後  |           |            |リソースの初期化、文書ツリーへのアクセスなど|
|   shouldComponentUpdate()    |          | 再描画前  |            |再描画前にアクセスしたいとき|
|       componentDidUpdate()        |          | 再描画後  |            |再描画後にアクセスしたいとき|
| componentWillUnmount() |          |           |   破棄時   |リソースの破棄など|
 

# ルーティング

```sh
npm install react-router-dom
または
yarn add react-router-dom
```

```jsx
import React from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";

export default function BasicRouter() {
  return (
    <Router>
      <div>

        {/* リンク表示部 リンクがここに表示される */}
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/about">About</Link>
          </li>
          <li>
            <Link to="/dashboard">Dashboard</Link>
          </li>
        </ul>

        <hr />

        {/* コンポーネント表示部 パスがマッチングしたコンポーネントがここに表示される */}
        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/dashboard">
            <Dashboard />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

function Home() {
  return (
    <div>
      <h2>Home</h2>
    </div>
  );
}

function About() {
  return (
    <div>
      <h2>About</h2>
    </div>
  );
}

function Dashboard() {
  return (
    <div>
      <h2>Dashboard</h2>
    </div>
  );
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88686/2138c6db-159a-d7c2-777a-319ab99a4ff2.png)


# ローカルストレージ

- ローカルストレージ
    - `値 = localStorade.getItem(キー)`
    - `localStorade.setItem(キー, 値)`
- JSON⇔オブジェクト変換
    - `オブジェクト = JSON.parse(JSON文字列)`
    - `JSON文字列 = JSON.stringify(オブジェクト)`
- ` || []` はnullの場合、`[]`にする。

```js
// データ取得
componentDidMount() {
  const todos = JSON.parse(localStorade.getItem('todos')) || [];
  this.setState({todos: todos});
}
// データ1件追加
addTodo(todo) {
  const todos = this.state.todos;
  todos.push(todo);
  this.setState({todos: todos});
  localStorade.setItem('todos', JSON.stringify(this.state.todos));
}
// データ1件削除
deleteTodo(i) {
  const todos = this.state.todos;
  todos.splice(i, 1);
  this.setState({todos: todos});
  localStorade.setItem('todos', JSON.stringify(this.state.todos));
}
```

# Fetch API

### Read List

```js
fetch("http://localhost:8080/todos")
.then((res) => res.json())
.then((data) => {
  this.setState({todos: data})
})
.catch((err) => console.log(err))
```

### Read 

```js
fetch("http://localhost:8080/todos/${id}")
.then((res) => res.json())
.then((data) => {
  this.setState({todo: data})
})
.catch((err) => console.log(err))
```

### Create

```js
fetch("http://localhost:8080/todos" ,{
  method: "POST",
  headers: { "Content-type": "application/json" },
  body: JSON.stringify(todo)
})
.then((res) => res.json())
.then((data) => {
  // 処理
})
.catch((err) => console.log(err))
```

### Update

```js
fetch("http://localhost:8080/todos/${id}" ,{
  method: "PUT",
  headers: { "Content-type": "application/json" },
  body: JSON.stringify(todos)
})
.then((res) => res.json())
.then((data) => {
  // 処理
})
.catch((err) => console.log(err))
```

### Delete

```js
fetch("http://localhost:8080/todos/${id}" ,{
  method: "DELETE"
})
.then((res) => res.json())
.then((data) => {
  // 処理
})
.catch((err) => console.log(err))
```

<details><summary>今後追加したいこと</summary>
<div>

- Redux
- Hook
- Spring Bootなどへの組み込み
</div>
</details>


参考
[速習 React 速習シリーズ Kindle版](https://www.amazon.co.jp/%E9%80%9F%E7%BF%92-React-%E9%80%9F%E7%BF%92%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E5%B1%B1%E7%94%B0%E7%A5%A5%E5%AF%9B-ebook/dp/B07GWFRCR1)
https://reactrouter.com/web/guides/quick-start