4
3

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 3 years have passed since last update.

KIT DeveloperAdvent Calendar 2019

Day 6

react-nativeでロード画面を表示させる

Posted at

はじめに

前回の記事からの続きです。
前回はstrageに画像をアップロードするまでをやりました。strageに画像をアップしてからURLを入手するまで少し時間がかかります。そこで、待ち時間にロード画面を表示するようにします。(concurrent modeは使いません)

コード

Camera.tsx
import React, { useRef, useState } from 'react';
import { NavigationStackScreenComponent } from 'react-navigation-stack';
import { View, StyleSheet, ActivityIndicator } from 'react-native';
import {
  Button, Container, Icon, Text,
} from 'native-base';
import { Camera as ExpoCamera } from 'expo-camera';
import { upLoadImg } from '../../../utils/upLoadImg';
import usePermission from '../../../utils/usePermission';

const styles = StyleSheet.create({
  button: {
    position: 'absolute',
    bottom: 100,
    zIndex: 1,
    alignSelf: 'center',
    height: 80,
    width: 80,
    flex: 1,
    justifyContent: 'center',
  },
  icon: {
    fontSize: 50,
  },
  flexOne: {
    flex: 1,
  },
  activityIndicator: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

const Camera: NavigationStackScreenComponent = () => {
  const { cameraPermission } = usePermission();
  const [isLoading, setIsLoading] = useState(false);

  const cameraRef = useRef(null);

  const snap = async () => {
    if (cameraRef) {
      const { uri } = await cameraRef.current.takePictureAsync(); // uriはローカルイメージURIで一時的にローカルに保存される
      const response = await fetch(uri);
      const blob = await response.blob();
      const imgName = blob.data.name;
      // console.log(blob.data.name);
      setIsLoading(true);
      upLoadImg(imgName, blob)
        .then((url) => {
          console.log(url);
          setIsLoading(false);
        });
      // .catch((error) => console.log(error));
    }
  };

  const renderCamera = (): React.ReactElement => {
    // console.log(cameraPermission);
    if (cameraPermission === null) {
      return <View />;
    } if (cameraPermission === false) {
      return <Text>No access to camera</Text>;
    }
    return (
      <Container style={styles.flexOne}>
        <ExpoCamera style={styles.flexOne} ref={cameraRef}>
          <View style={styles.flexOne}>
            <Button
              rounded
              icon
              onPress={() => snap()}
              style={styles.button}
            >
              <Icon name="camera" style={styles.icon} />
            </Button>
          </View>
        </ExpoCamera>
      </Container>
    );
  };

  const renderActivityIndicator = () => (
    <View style={styles.activityIndicator}>
      <ActivityIndicator />
    </View>
  );

  return (
    <>
      {
        isLoading
          ? renderActivityIndicator()
          : renderCamera()
      }
    </>
  );
};

export default Camera;

isLoadingというステートを用意しておいて、ロードが始まったらisLoadingをtrueに、終わったらfalseにするというようにして、isLoadingによってロード画面を表示しています。
そんなに大変な作業ではないですね。そうなんです、記事を分ける必要はなかったんです( ´•д•`; )
この記事の本題はここまでですが、僕はこれから尺を稼ぎます。
よかったら読んでください、、、

フックについて

今回はreactのhooksやカスタムフックを使いましたが、hooksについてまとめます。

フックとは

フックはreactの機能の一つで、ステートやライフサイクルなどの機能をクラスを書かずに使えるようにします。react-nativeでは、React Native0.59リリース以降でフックをサポートしています。
一部のコンポーネントだけでフックを使うこともでき、クラスコンポーネントと共存させることができます。(クラスコンポーネント内ではフックを利用することはできません)

フックのルール

  1. 通常のjavascript関数内から呼び出してはいけない(呼び出すのはfunctional componentのみ)
  2. hooksは条件分岐やネストされた関数内から呼び出すことはできない(react関数内のトップレベルでのみ呼び出す)

一つ目のルールについては二つ目のルールを違反しないためのルールだと思います。
二つ目の理由についてはこの記事がとてもわかりやすかったです。
とにかくreactはフックが呼ばれる順番に依存しているということを覚えておけば良さそうです。

カスタムフックを作る時の注意点

カスタムフックとは、名前がuseで始まり、ほかのフックを呼び出すJavaScriptの関数のことです。(javaScriptの関数内から呼び出せるんかいって思いましたが、他のフックを呼び出した時点でそれはもうJavaScript関数ではなくてカスタムフックになります。)
関数名をuseで始める理由は、その関数にフックのルールが適用されるということを知らせるためです。これによってカスタムフックを利用する時に明示的にトップレベルで呼び出すだけで良くなります。(useから始めなくても普通に動きます。)
フックにはルールがあるからフックを使っているロジックをコンポーネント間で共有するときは気をつけようということだと思います。

functional components VS stateless components

ちなみに、こんな関数👇は以前までstateless componentsと呼ばれていましたが、hooksが登場した時点でこれらはfunctional componentsと呼ぶようになりました。

import React from 'react';

const Hoge = (props) => (
  <h1>{props.name}</h1>
);

function Hoge(props) {
  return (
    <h1>{props.name}</h1>
  );
};

あと、functional componentで”import React from 'react';”するのなんでなのか、ずっと気になっていました。
functional componentでreactをインポートする理由は、インポートしないと”React.createElement()”が使えないからです。上記のコードではh1タグのようなものが出てきましたが、これはJSXと呼ばれるもので、このJSXを使うとreactは自動的にReact.createElement()によって**"react要素"**と呼ばれるものに変換しています。

babelによってこんな感じ👇に変換されるのですが、その時にReact.createElement()が必要になります。reactを書いているときはbabelによって変換される前のコードしか見ていないため分かりずらいですね。ぜひbabelのアレで試してみてください。

// 変換前
function hoge() {
  return <div className="container">hello world</div>;
}

// 変換後
function hoge() {
  return React.createElement("div", {
    className: "container"
  }, "hello world");
}

途中で出てきた誰やねんって感じの"react要素"ですが、react要素と”ルートDOMノード”をReactDOM.render()に渡すことで画面が表示されます。

// .html
// ルートDOMノード
<div id="root"></div>

// .js .jsx
import React from 'react';
import ReactDOM from 'react-dom';
// JSX
const element = <h1>Hello, world</h1>; 
ReactDOM.render(element, document.getElementById('root'));

reactの環境をずっとcreate-react-appで整えてきたためこの辺りはあまり知りませんでした。babelやらwebpackやらは名前かっこいいくらいにしか思っていなかったため、自力でreactの環境を整えながら勉強しようと思いました。

最後に

最後まで読んで頂きありがとうございます。締めの一言的なものが何も思い浮かばないため、世界一どうでもいい情報を発表しときます。
最近ボクサーからトランクスに乗り換えました!!

4
3
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
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?