5
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

React Native + Expo アプリで QRコードを読んでJSON取得

Posted at

この記事は、「【連載】初めてのReact Native + Expo開発環境構築入門」の子記事です。環境などの条件は、親記事をご覧ください。


 前回までに、請求書情報をサーバーから取得して表示できるようになったので、今回は呼び出す先のサーバー情報をQRコードから取得できるようにします。

 QRコードにあらかじめJSONを仕込んでおけば、複数の設定項目がある設定をアプリの設定方法説明書なしでユーザーに実施してもらえるようになるなど、利便性アップや業務の効率化が見込めます。

【注意】現時点で、QRスキャナ機能がうまく動作しない場合があるようです。詳しくはフォーラム参照。

目標:Importボタンを押す → QRコードリーダー → QRに入ってるURLから請求書を読み込み
image.png image.png image.png

QRリーダーをインストール

 BarCodeScannerモジュールは、QRだけでなくいろんな企画のバーコードも読み込めるバーコード・QRリーダーです。さくっとインストールします。Visual Studio CodeでReact Nativeのプロジェクトを開き、Ctrl + @でPowerShellを開いて以下のコマンドを実行。

expo install expo-barcode-scanner

QRコード読み込み画面を設置

 QRコード読み込み画面は、ExpoのバーコードAPIの例からコピーして、unstatedのためのHOC化を実施します。

/components/BarcodeScannerScreen.js
import * as React from "react";
import { Text, View, StyleSheet, Button } from "react-native";
import Constants from "expo-constants";
import * as Permissions from "expo-permissions";
import { Subscribe } from "unstated";

import { BarCodeScanner } from "expo-barcode-scanner";
import InvoiceContainer from "../containers/InvoiceContainer";

class BarcodeScannerScreenContent extends React.Component {
  state = {
    hasCameraPermission: null,
    scanned: false
  };

  async componentDidMount() {
    this.getPermissionsAsync();
  }

  getPermissionsAsync = async () => {
    const { status } = await Permissions.askAsync(Permissions.CAMERA);
    this.setState({ hasCameraPermission: status === "granted" });
  };

  render() {
    const { hasCameraPermission, scanned } = this.state;

    if (hasCameraPermission === null) {
      return <Text>Requesting for camera permission</Text>;
    }
    if (hasCameraPermission === false) {
      return <Text>No access to camera</Text>;
    }
    return (
      <View
        style={{
          flex: 1,
          flexDirection: "column",
          justifyContent: "flex-end"
        }}
      >
        <BarCodeScanner
          onBarCodeScanned={scanned ? undefined : this.handleBarCodeScanned}
          style={StyleSheet.absoluteFillObject}
        />

        {scanned && <Button title={"Tap to Scan Again"} onPress={() => this.setState({ scanned: false })} />}
      </View>
    );
  }

  handleBarCodeScanned = ({ type, data }) => {
    this.setState({ scanned: true });
    // alert(`Bar code with type ${type} and data ${data} has been scanned!`);
    let jsondata = {};
    if (this.isJson(data) && (jsondata = JSON.parse(data)).invoice_endpoint_url) {
      this.props.globalState.getDataFromServer(jsondata.invoice_endpoint_url);
      this.props.navigation.goBack();
    } else {
      alert("This is not a valid QR code.");
    }
  };

  isJson(str) {
    try {
      JSON.parse(str);
    } catch (e) {
      return false;
    }
    return true;
  }
}

const BarcodeScannerScreen = ({ navigation }) => {
  return (
    <Subscribe to={[InvoiceContainer]}>
      {globalState => <BarcodeScannerScreenContent globalState={globalState} navigation={navigation} />}
    </Subscribe>
  );
};

export default BarcodeScannerScreen;

 unstatedとnavigationのためにHOC化以外に実施していることは、QRコードを読み込んだら中身をチェックし、もしJSONで、さらに中にinvoice_endpoint_urlがあったら指定URLから請求書読み込み実施して画面を戻る、そうでなければ「This is not a valid QR code.」とアラート、です。アラートの場合、navigationのBackボタンで画面を戻ることもできるし、「Tap to Scan Again」ボタンで再挑戦することもできます。

 また、グローバルStateのInvoiceContainerのgetDataFromServer()をコールするときに、QRコードから受け取ったinvoice_endpoint_urlを渡すことにしました。このため、InvoiceContainerの修正も必要になります。

 作った画面コンポーネントをnavigationに登録。

App.js
...
import BarcodeScannerScreen from "./components/BarcodeScannerScreen";
...
const RootStack = createStackNavigator(
  {
    Home: HomeScreen,
    InvoiceEdit: InvoiceEditScreen,
    Summary: SummaryScreen,
    BarcodeScanner: BarcodeScannerScreen
  },
  {
    initialRouteName: "Home"
  }
);
...

指定されたURLから請求書を読み込む動作を実装

 請求書取得のエンドポイントはもともとINVOICE_API_ENDPOINTに定数化していましたが、引数で受け付けるように変更します。

containers/InvoiceContainer.js
...
// const INVOICE_API_ENDPOINT = "http://192.168.1.8:8080/invoice.js";
...
  getDataFromServer(endpoint) {
    this.setState({ isDataLoading: true });
    axios
      .get(endpoint, { timeout: 3000 })
...

※ついでに通信タイムアウトを3秒に設定しました。

JSONにエンドポイントURLが入ったQRコードを作成

 オンラインQRコード生成アプリ QR Code Generatorなどで、以下のJSONをQRコード化。※ここで指定するURLは、前回の記事の請求書JSONファイルが取れるアドレスを指定します。

{"invoice_endpoint_url":"http://192.168.1.8:8080/invoice.js"}

image.png

請求書読み込みボタンでQRスキャン画面に飛べるようにする

 HomeScreenのImportボタンで、QRリーダー画面に飛べるようにします。(以前の動作はコメントアウト)

components/HomeScreen.js
  onImportClick() {
    // this.props.globalState.getDataFromServer();
    this.props.navigation.navigate("BarcodeScanner");
  }

 コンポーネントではなくグローバルState側にサーバーとの通信動作を記述しているので、変更はとても簡単ですね。

結果

image.png

Importボタンを押すとQRスキャン画面へ。

image.png

間違ったQRコード(やバーコード)を読むと、警告。

image.png

正しいQRを読み込むと、サーバーの請求書を読み込んで元画面に遷移。

image.png

5
9
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
5
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?