かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

TypeScript スプレッド構文で配列に変換しようとしたらエラーになった

TypeScript で [...document.querySelectorAll('meta')] こんな感じにスプレッド構文で NodeList を配列に変換しようとしたらエラーが出てしまいました。

Type 'NodeListOf<HTMLMetaElement>' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators.

₍ᐢ •̥ ̫ •̥ ᐢ₎‪ なんもわからん

tsconfig の compilerOptions の target が es5 の時は downlevelIteration オプションと dom.iterable が必要

{
  "compilerOptions": {
    "target": "es5",
    "downlevelIteration": true, // 追加
    "lib": [
      "dom",
      "dom.iterable",  // 追加
      "esnext",
    ],
   // …
  }
}  

target: es6 の時は lib に dom.iterable だけあれば OK っぽい。

₍ ᐢ. ̫ .ᐢ ₎ 俺 たち は雰囲気で TS を使っている…


[参考]

まだ積んでる…

React TypeScript useState に型を設定したい

React の useState で返される state の型を指定したい

useState<型>(initial value) で state に型を強制できる

type IdolType = 'cute' | 'cool' | 'pop' | 'sexy';

const [type, setType] = useState<IdolType>('cute');
setType('foo');
// => Argument of type '"foo"' is not assignable to parameter of type 'SetStateAction<IdolType>'

setType()IdolType の型以外の値を設定しようとするとエラーになる

Boolean しか入らないような state は直接 <boolean> と書ける

const [isHoshimiya, setIsHoshimiya] = useState<boolean>(true);

おわり


[参考]

実践TypeScript

実践TypeScript

React axios でファイルアップロードしたい

フロントを React バックエンドを PHP で作成していて、ファイルのアップロードを試してみたのでメモ

File を扱うコンポーネント

React では、<input type="file" /> は値がユーザだけが設定できるものでありプログラムでは操作できないため、常に非制御コンポーネントです。
cf. 非制御コンポーネント – React

input[type="file"]value はプログラムで設定できないので値は FileAPI を使って取得する必要があるトノコト。

input[type="file"]value は設定できないけど、onChange で選択されたファイルについては files[0] で取得できる Fileオブジェクト を state として持つことはできるっぽい

function InputFile() {
  const [file, setFile] = useState(null);
  
  const onChangeHandler = useCallback((e) => {
    // ファイル選択がキャンセルされた時は undefined
    const file = e.target.files[0];
    setFile(() => {
      return file ? file : null;
    });
  }, []);

  return <input type="file" accept="image/*" onChange={onChangeHandler} />
}

state でファイルオブジェクトを持ってしまうので、公式リファレンスにあるように useRef を使って必要なときだけ ファイルオブジェクトを取り出すようにした方がメモリ効率は良いのかも知れない。

File を含むデータを axios で送信する

以前書いた記事 JavaScript (SPA) PHP axios でフォームデータを送る時に気をつけること - かもメモ でバックエンドの PHP$_POST でフォームデータを取得できるようにするには URLSearchParams 形式で送れば OK と書いていたのですが、URLSearchParams で送った場合 $_FILESnull になってしまいうまく取得できませんでした。

ファイルを含むフォームデータは FormData 形式の Content-Type: 'multipart/form-data' で送るのが良いみたいです。

フロント

function PostForm() {
  const ref = useRef(null);

  const onPostForm = useCallback(async(data) => {
    try {
      // Object の data を FormData 形式に変換する
      const params = new FormData();
      Object.keys(data).forEach(function(key) {
        params.append(key, this[key]);
      }, data);
      const res = await axios.post(API_URL, params, {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
      });
      console.log(res);
    } catch(err) {
      console.log(err);
    }
  }, []);
  
  const onSubmitHandler = useCallback((e) => {
    e.preventDefault();
    onPostForm({
      file: ref.current.files[0],
    });
  }, [onPostForm]);

  return (
    <form onSubmit={onSubmitHandler}>
      <input type="file" accept="image/*" ref={ref} />
      {/* 略 */}
    </form>
  );
}

バックエンド (PHP)

<?php
$file = $_FILES['file'];

header('Content-Type: application/json; charset=utf-8', true, 200);
echo json_encode([
  'file_name' => $file['name'],
  'tmp_file' => $file['tmp_name'],
  'error' => $file['error'],
], JSON_UNESCAPED_UNICODE);

FormData 形式・Content-Type: 'multipart/form-data' で送信すれば PHP$_FILES で送られたファイルデータを取得することができました!
₍ ᐢ. ̫ .ᐢ ₎ ヤッタネ!!


[参考]