🐥 React × TypeScript でのメモアプリ作成 #4 (スプレッド構文・map・!)
作成日: 2022/02/04
2

学習メモ

スプレッド構文

  • 配列やオブジェクトに対して使える記法

  • ... という形でドットを3つ繋げて使用する

MDN - スプレッド構文

let numberStore = [0, 1, 2];
let newNumber = 12;
numberStore = [...numberStore, newNumber];

上記の例では、最後の行を何度でも再実行して、配列の最後に 12 を追加し続けることができます。

Reactでmapを使う理由

なぜReactでは配列のmapメソッドを用いるか

  1. 配列にJSXの要素を入れて{}で囲むだけで一覧表示ができるから
class App extends React.Component {
    render() {
        const arr = [<li>リンゴ</li>, <li>パイナップル</li>, <li>ペン</li>];
        return (
            <ul>
                {arr}
            </ul>
        );
    }
}
  1. mapメソッドが、戻り値として新しい配列を返すから

<li>リンゴ</li> のように要素をタグで囲むのは面倒なので、mapメソッドを使う
mapメソッドは、配列内の各要素に対して関数を呼び出し、その結果から新しい配列を作る

MDN - Array.prototype.map()

const array1 = [1, 4, 9, 16];

// pass a function to map
const map1 = array1.map(x => x * 2);

console.log(map1);
// expected output: Array [2, 8, 18, 32]

mapを使うことによって、 1.のコードは以下のように記述できる

class App extends React.Component {
    render() {
        const arr = ["リンゴ", "パイナップル", "ペン"];
        return (
            <ul>
                {arr.map((fruit, i) => <li key={i}>{fruit}</li>)}
            </ul>
        );
    }
}

※ Reactでは{}内の配列を一覧表示する際には key という属性を付与することが義務付けられているため、mapメソッドの第二引数に渡されてくる配列要素のインデックス ikey として流用している

非Nullアサーション (non-null assertion operator)

フォームに入力した値をリセットするところの処理を以下のようにしたところ

inputElement.current.value = "";

TypeScriptのエラーが出た

Object is possibly 'null'.

前回のようにnullが入る可能性があることを教える

inputElement.current?.value = "";

別のエラーになった

The left-hand side of an assignment expression may not be an optional property access.

inputElement.current.value が空だと値を入れられない可能性があるため、エラーになるらしい

!を付けてnullにならないことを教える

inputElement.current!.value = "";

サバイバルTypeScript - ! 非Nullアサーション (non-null assertion operator)

値がnullやundefinedでないことを宣言し、コンパイラーに値を非Nullとして解釈させる

追加したメモをリスト表示するところまで完成

import classes from "./styles/App.module.scss";
import { useState, useRef } from "react";

export const App = () => {
  const inputElement = useRef<HTMLInputElement>(null);
  const [outputText, setOutputText] = useState<string[] | null>([]);

  const onClickAddButton = () => {
    if (inputElement.current?.value === "") return; // フォームに入力された文字が空白の場合は何もしない

    // フォームに入力した値を配列に入れる
    const newTexts = [...outputText!, inputElement.current!.value];
    setOutputText(newTexts);

    // フォームに入力した値をリセットする
    inputElement.current!.value = "";
  }

  return(
    <div className={classes.container}>
      <h1 className={classes.title}>簡単メモアプリ</h1>

      <div className={classes.inputArea}>
        <input ref={inputElement} className={classes.inputAreaTextArea} type="text" placeholder="メモを入力" />
        <button onClick={onClickAddButton} className={classes.inputAreaButton}>追加</button>
      </div>

      <section className={classes.outputArea}>
        <h2 className={classes.outputAreaTitle}>メモ一覧</h2>
        <ul className={classes.outputAreaList}>
          {
            outputText!.map((listItem: string, i: number) => 
              <li className={classes.outputAreaListItem} key={i}>
                <span>{listItem}</span>
                <button>✕</button>
              </li>          
            )
          }
        </ul>
      </section>
    </div>
  );
};

所感

相変わらず TypeScript にやられています。
意味のないエラーを吐いているんじゃないかと毎回思うのですが
調べてみるとたしかに正しいことを言っていて、頭が上がりません🛌