LoginSignup
9
6

More than 1 year has passed since last update.

React.js (TypeScript) で、QR コードを読み込んでフォームにセットする

Last updated at Posted at 2022-10-31

やりたいこと

React.js (TypeScript) で、form の text フィールドの入力を、QR コードを読み込んで自動入力したい。form は、react-hook-form を使いたい。

Screen Shot 2022-10-31 at 15.21.01.png

QRコードマークをタップすると、カメラが立ち上がり、QRコードを認識したら text 情報をテキストフィールドにセットして、カメラが閉じる。カメラは、Modal を立ち上げるようにした。react-modal で。

QR Code Reader のパッケージ検討

react-qr-reader は、バージョンが、3.0.0-beta-1 という斬新なバージョニングで、README のサンプルコードは動かないし、動かしてみたけど、読み込みの感度が微妙だったりと、私的にいまいちだったのに対し、react-zxing は、簡単にすんなり動き、感度もそこそこだったので、今回はそれを使うことにした。

react-zxing

Github の Usage に書いてある

import { useState } from "react";
import { useZxing } from "react-zxing";

export const BarcodeScanner = () => {
  const [result, setResult] = useState("");
  const { ref } = useZxing({
    onResult(result) {
      setResult(result.getText());
    },
  });

  return (
    <>
      <video ref={ref} />
      <p>
        <span>Last result:</span>
        <span>{result}</span>
      </p>
    </>
  );
};

これをそのまま使えば、QR コードの読み取りは完了する。TypeScript への対応も、useState<string>("") にしとくかくらいで、ほとんどやることはない。react-qr-reader みたいな、インカム起動するのかよ!みたいなのもない。簡単すぎる!!

が、これで yarn start すると、めちゃくちゃエラーが出る。

.env
GENERATE_SOURCEMAP=false

私は、SOURCE MAP必要ないので、これで。

インストール等

$ yarn create react-app qr_reader --template typescript
$ cd qr_reader
$ yarn add react-hook-form react-modal react-zxing @emotion/react
$ yarn add @types/react-modal --dev

QRコードのアイコンは、iconmonstr から使わせていただきました。

完成

src
├── components
│   ├── ScanModal.tsx // モダル(App.tsx から呼ばれる)
│   └── QrReader.tsx  // QR コードリーダー(ScanModal から呼ばれる)
└── App.tsx // Form

とする。手抜きだけど。

App.tsx

App.tsx
/** @jsxImportSource @emotion/react */
import React, { useState } from "react";
import { css } from "@emotion/react";
import { useForm } from "react-hook-form";
import { ScanModal } from "./components/ScanModal";

function App() {
  // Modal の開閉
  const [isOpen, setIsOpen] = useState<boolean>(false);
  const onClickToggle = () => {
    setIsOpen(!isOpen);
  };
  // 読み込んだ QR コードのテキスト情報を格納
  const [result, setResult] = useState<string>("");

  // form の設定・処理
  const { handleSubmit, register } = useForm();
  const onSubmitForm = (formData: any) => {
    console.log(formData);
  };

  const container = css`...`;
  const formStyle = css`...`;
  const qrButton = css`...`;

  return (
    <div css={container}>
      <form onSubmit={handleSubmit(onSubmitForm)} css={formStyle}>
        <input type='text' {...register("code")} defaultValue={result} />
        <button type='submit'>submit</button>
      </form>

      <button onClick={onClickToggle} css={qrButton}>
        <img src='/qr-code.png' alt='scan qr code' />
      </button>
      <ScanModal
        isOpen={isOpen}
        onRequestClose={() => setIsOpen(false)}
        setResult={setResult}
      />
    </div>
  );
}

export default App;

QRコードリーダーで読み込んだテキストは、result に格納される。これを text フィールドの defaultValue にセットしている。

  const [result, setResult] = useState<string>("");
...
        <input type='text' {...register("code")} defaultValue={result} />

QrReader.tsx

QrReader.tsx
import { useZxing } from "react-zxing";

type Props = {
  setResult: React.Dispatch<React.SetStateAction<string>>;
  onRequestClose: () => void;
};

const QrReader = ({ setResult, onRequestClose }: Props) => {
  const { ref } = useZxing({
    onResult(result) {
      setResult(result.getText());
      onRequestClose();
    },
  });

  return <video ref={ref} />;
};

export default QrReader;

サンプルコードの setStateAction が外だしになっているのと、onResult に react-modal を閉じるために渡している onRequestClose が追加されている。何か情報が入ってきたら、setResult して、モダルを閉じる。

よく、QRコードを認識したら、認識した QR コードを border で囲うみたいな演出があるけど、今回はすぐ閉じちゃうので検討外。やるなら、result.getResultPoints() などを利用すればできるものと思われる。

ScanModal

ただの react-modal なので、省略。
かっこよく装飾したいとかあれば、デザインを当てればよいと思われる。

実機確認

スマホでカメラにアクセスするには、https になっている必要があるので、https で yarn start する。

$ HTTPS=true yarn start

こうすると、

Compiled successfully!

You can now view qr_reader in the browser.

  Local:            https://localhost:3000
  On Your Network:  https://192.168.1.13:3000

Note that the development build is not optimized.
To create a production build, use yarn build.

webpack compiled successfully
No issues found.

と、出てくるので、

  On Your Network:  https://192.168.1.13:3000

今回だと、https://192.168.1.13:3000 に同じネットワーク上であれば、違うデバイスからもアクセスできる。
SSLが信用ならんとか、カメラアクセス許可しますかとかは、進んでもらうと、以下動画のような感じで、やりたいことが実現できる。

動画は、Google Pixel6 でやっているけど、iPhone でももちろんできるし、PC でもできる。でも、iPhone 14Pro との比較だと Pixel6 のほうが感度が抜群に良かった。iPhone でしばらく読み込めない場合は、横向きにしたり刺激を与えると反応する。なんちゃってSSLだから?明るさ?詳しい人いたら教えてください。

こういうことをやりたい人は少ないのか、あんまり情報がなかったり、バージョンが古いと新しいものとの互換性がなかったりしてましたが、いったん目的のものは出来たので、満足しました。おしまい。

9
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
6