20201113のReactに関する記事は8件です。

expoで作成したreact nativeアプリをios向けにテスト配布する方法

はじめに

expoで作成したiOSアプリを開発者以外に配布したいという場面で苦戦したのでまとめました。

主に方法は2つ (他にもあればご教授お願い致します)

expoでpublishする(無料)
Testflight/DeployGateを利用する(Apple Developer Programへ入る必要あり)

expoでpublishする(無料)

expo publishコマンドを実行するとアカウントのProjectページにビルドされたアプリが表示されます。

ここで詰まったのが、Projectページに表示されているQRコードをスキャンしたが読み込みに失敗した。

image.png

QRコードの真下に書いてあるのだが、この機能が使えるのはandroid端末のみ。

iOSはpublish先のexpoアカウントにログインしていないと使えないらしい。
チームの共有のアカウントを作って、そこにpublishするのが良いかと思います。

TestFlightやDeployGateを利用する

.ipaファイルをアップロードする事でテスト配布する事ができます。

expo build:ios

を実行すれば.ipaファイルが作成できますが、Apple Developer Program(年会費$99が必要)に入会していなければ実行できません。

DeployGate
TestFlight

まとめ

自分は今回ハッカソン用に配布したかっただけで、Developer Programに入る程ではなかった為アプリの配布に少し苦戦しました。
同じexpoアカウントに複数人でログインするというのに少し抵抗がありましたが、調べてみた結果これ以外方法が無いかなと思いました。(ハッカソンという時間の問題もあり)

ストアへの公開まで考えている方はApple Developer Programに入るのが確実かなと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Styled Componentを使ってアコーディオンを実装する方法【react.js, typescript, javascript, styled-compoent】

TODO

Styled-Componentを使ってアコーディオンアニメーションをさくっと実装
https://styled-components.com/

CODE

index.ts
const Accordion = styled.div`
    max-height: 0;
    overflow: hidden;
    max-height: ${props => (props.isHidden ? '0' : '500px')};
    transition: max-height 1s;
`
index.ts
function ComponentName({isHidden, children}: Props) {
     return (
       <>
         <div style={{ overflow: 'hidden' }}>
           <Accordion isHidden={isHidden}>
              {children}
           </Accordion>
         </div>
      </>
    )
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Styled Componentを使ってアコーディオンを実装【react.js, typescript, javascript, styled-compoent】

TODO

Styled-Componentを使ってアコーディオンアニメーションをさくっと実装

CODE

index.ts
const Accordion = styled.div`
    overflow: hidden;
    max-height: ${props => (props.isHidden ? '0' : '500px')};
    transition: max-height 1s;
`
index.ts
function ComponentName({isHidden, children}: Props) {
     return (
       <>
         <Accordion isHidden={isHidden}>
              {children}
         </Accordion>
      </>
    )
}

REFERENCE

https://styled-components.com/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactを学ぶVIII 〜ライフサイクルUpdateとUnmountを使う〜

■ はじめに

タイトルについて記事にしました。
この記事で得る内容は以下の通りです。

・ Reactの基礎知識が増える
・ ライフサイクル(Update及びUnmount)の実演

■ 例(前回の続き)

前回、いいねボタンを作って、ボタンをクリックするとカウントアップした所までやりましたので
次にライフサイクル(Update及びUnmout)を実際に使ってみたいと思います

カウントアップされていく.gif

■ componentDidUpdate使用例

内容は、いいね数が10以上になると0に戻るという処理にします

Blog.jsx
componentDidUpdate() {
  if (this.count.state >= 10) {
  this.setState({count:0});
  }
}

いいね数が10以上になると0に戻りました

componentDidUpdate.gif

■ componentWillUnmount記述例

ページを変えるとイベントリスナーが不要になるのでaddだった所をremoveに変えます

Blog.jsx
componentWillUnmount() {
  document.getElementById("counter").removeEventListener("click",this.countUp);
}

リスナーの解除を行う事で、リソースが開放され、サーバーに負荷が掛からなくなります

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[React]コンポーネントを作るときに他からコピーしてくる作業を自動化するCLIツールを作る

TL;DR

Reactでコンポーネントを新しく作るとき、Storybookやテストも作成するのであれば、ディレクトリを作成してから、
index.ts, Component.tsx Component.stories.tsx Component.spec.tsx
と4ファイルくらい作成することになると思いますが、
毎回これらを手動で作成して、他のコンポーネントからimport文とかをコピーしてくるのは面倒なので、CLIツールを作成して、コマンド一発で出来るようにします。

コードはこちら

作ったもの

ディレクトリ構成

AtomicDesign想定です。

.
├── package.json
├── src
│   └── components
│       └── atoms
│           └── Button
│               ├── Button.spec.tsx
│               ├── Button.stories.tsx
│               ├── Button.tsx
│               └── index.ts
└── tools
    ├── componentTemplates
    │   ├── Component.tsx
    │   ├── Spec.tsx
    │   ├── Story.tsx
    │   └── index.ts
    └── createComponent.js

コード

テンプレート作成

まず、環境に合わせて ./tools/componentTemplates/ 以下に、作成したいファイルのテンプレートを配置します。
このテンプレートをリネームして、必要な場所にコピーする感じです。

テンプレートはこちらを参照してください。

script作成

コマンドライン引数 オプション -C で、コンポーネントの粒度とコンポーネント名を受け取ります。

以下はコマンドのhelpです。

Usage: create-component [options]

Options:
  -C, --component [dir/Component]  The name of the component to be created (ex) atoms/Button
  -h, --help                       display help for command
  • 引数が不正である場合はエラー(atoms, molecules, organismsしか許容しない)
  • すでにコンポーネントが存在しているときはエラー(上書きしない)
  • ディレクトリが存在しないとき(/molecules/ など未作成の場合)は新規作成

します。

コードは以下です。

const { program } = require("commander");
const fs = require("fs");

program.option(
  "-C, --component [component name]",
  "The name of the component to be created (ex) atoms/Button"
);

program.parse(process.argv);

if (!program.component) {
  program.help();
}

const argument = program.component.split("/");
const dir = argument[0];
const component = argument[1];

if (!["atoms", "molecules", "organisms"].includes(dir)) {
  console.error(
    "ERROR: Only 'atoms', 'molecules', and 'organisms' can be specified."
  );
  return;
}

// If the directory is not yet created, create it.
if (!fs.existsSync(`./src/components/${dir}`)) {
  fs.mkdirSync(`./src/components/${dir}`);
}

const templates = [
  "./tools/componentTemplates/index.ts",
  "./tools/componentTemplates/Component.tsx",
  "./tools/componentTemplates/Story.tsx",
  "./tools/componentTemplates/Spec.tsx",
];

const dests = [
  `./src/components/${dir}/${component}/index.ts`,
  `./src/components/${dir}/${component}/${component}.tsx`,
  `./src/components/${dir}/${component}/${component}.stories.tsx`,
  `./src/components/${dir}/${component}/${component}.spec.tsx`,
];

fs.mkdirSync(`./src/components/${dir}/${component}`);

// Error when a component already exists
templates.forEach((template, index) => {
  fs.copyFileSync(
    template,
    dests[index],
    fs.constants.COPYFILE_EXCL,
    (error) => {
      if (error) {
        throw error;
      }
    }
  );
  console.log(`✨ Create component template ${dests[index]}`);
});

package.jsonに追記

package.jsonのscriptsに、

    "create-component": "node ./tools/createComponent.js"

を追記して完了です。

使用例

$ yarn create-component -C atoms/Icon

✨ Create component ./src/components/atoms/Icon/index.ts
✨ Create component ./src/components/atoms/Icon/Icon.tsx
✨ Create component ./src/components/atoms/Icon/Icon.stories.tsx
✨ Create component ./src/components/atoms/Icon/Icon.spec.tsx

これでめんどくさい作業から開放されました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React Native】Invariant Violation "main" has not been registered. This can happen if: *Metro (the local dev server ... の解決法

はじめに

自分の場合はとても処方的なミスでした (react-navigationが関係)
他にもいくつか試した対処法も載せてあります。

expoで開発していたら以下の様なエラーが起きた。

Invariant Violation "main" has not been registered. This can happen if: *Metro (the local dev server) 
is run from the wrong folder. Check if Metro is running, stop it and restart it in the current project.*
A module failed to load due to an error and `AppRegistry.registerComponent` wasn't called.

環境

// app.json
   {
    "sdkVersion": "38.0.0"
   }
// package.json
  "dependencies": {
    "expo": "^38.0.8",
    "react-native": "^0.62.2",
    "react-navigation": "^4.4.3",
    "react-navigation-stack": "^2.10.0",
    "react-navigation-tabs": "^2.10.1",
   }

試した対処法

調べてみるといくつか方法があった

1つめ

  1. watchman watch-del-all
  2. node_modules & package-lock.json (もしくはyarn.lock) の削除
  3. npm install (もしくはyarn install)
  4. expo install react-native-safe-area-context
  5. expo start

試してみたが画面は変わらず。。

2つめ

  1. expoを止めている状態で、ps aux | grep react-native を実行して動いているプロセスを探す
  2. 結果が表示されたら、そのプロセスを sudo kill 'pid'

自分は動いてるプロセスは見つけたものの、killすると
kill:'pid': No such process
と出て解決には至らず。。

3つめ

package.jsonのnameとApp.jsonのexpoのnameを同じにする

→ 何も変わらず

自分の解決策

結局めちゃくちゃ初歩的なミスでした。。
react-navigationを使っている部分で、 createAppContainerを呼んでいるが、肝心のimportをし忘れていた為、エラーが起こっていた様です。

今回のような場合だけでなく

// ここを書き忘れていた
// import { createAppContainer } from "react-navigation";

 const AppContainer = createAppContainer(HomeTab);

普段こんな場合は
ReferenceError: Can't find variable: Button
とかでエラーが出ると思うのですが。。。

importの部分を書き足すと正常に動作しました。

ルーティング周りで変更を加えた場合は、そこの箇所を戻してみると治るかもしれません。

2020/11/14 追記
export default class Index extends React.Component{}

この部分を

export default class Index extends React.Component(){}

と不要な()をつけてしまった為、同じエラーが起きた。

おそらく特定のパターンに絞れないエラーなので、まずはコードに間違いが無いか確認してみると良いと思います。

メモ

expo start --no-dev --minifyで起動すると解決する」
とexpoのForumsにあったので、試してみると確かに一時的に解決した。
Forums

しかしこれはproduction用にアプリをminifyして不要な部分を削っている為、部分的に解決しただけだと思われる。
expo -Development and Production Mode

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

子供用の学習アプリケーションを作る(1)

はじめに

昨日、筋萎縮性側索硬化症(ALS)と呼ばれる病気についての記事を見かけ、以前より知っている病気ではあったもののなぜ、この病気になってしまうのかについては、知らなかった。
ネットで調べてみたところ、様々なサイトで解説されているものの、言葉が難しかったり、文字数が多く読むのが大変であった。

そこで、そのような知識を分かりやすく、まとめ尚且つ大人から子供まで使えるような学習アプリケーションを自作してみました。

実装

まず、環境構築については、下記の記事を見ていただければと思います。
参考: React Nativeをはじめる(環境構築)

ディレクトリ構成

使用したUI component, ライブラリ, フリーイラスト

UI component
1.React-native-elements
React-Native-Elementsは、フリーのクロスプラットフォームのUIcomponentです。
色々な開発者が作成したcomponentが集められ、クオリティもかなり高いです。

2.React-native-paper
React-native-paperは、使いやすく設計されているUIcomponentです。
componentは、Googleのマテリアル基準に準拠しているのでデザイン性がかなり高いという感想です。

ライブラリ
1.React Navigation
React Navigationは、画面遷移のライブラリです。
なぜ、このライブラリが必要かというと、Reactでは画面遷移のための機能が用意されていないからです。

React Navigationでは3つの方法で画面遷移ができます。

1.stack:スライドしながら画面遷移する方法
2.tabs:タブを選択することで画面遷移する方法
3.drawer:画面端から現れるメニューで画面遷移する方法

今回は、1の方法で実装を行いました。

フリーイラスト
1.manypixels
manypixelsは、フラットなイラストをフリーでdlできます。

App.js

このファイルではメインとなるページの処理を記載しています。

App.js
import 'react-native-gesture-handler';
import * as React from 'react';
import {Image, SafeAreaView, ScrollView, StyleSheet} from 'react-native';
import {Text, Card, Button} from 'react-native-elements';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';
import AppHeader from './components/header';
import contentsSelect from './components/contentsSelect';

const HomeScreen = ({navigation}) => {
  return (
    <SafeAreaView style={styles.container}>
      <ScrollView>
        <AppHeader />
        <Text h2 style={styles.hearderMessage}>
          For explorer who want to know the unknown
        </Text>
        <Text style={styles.hearderMessageBody}>
          ilearnはさまざまなトピックスをまとめ{'\n'}発信する場所だよ
        </Text>

        <Card>
          <Card.Title>探検をはじめよう</Card.Title>
          <Card.Divider />
          <Image
            source={require('../public/img/teach.png')}
            style={styles.cardImage}
          />
          <Text style={styles.cardText}>
            医学,科学,数学,コンピュータサイエンス,宇宙,{'\n'}歴史をまなぼう
          </Text>

          <Button
            style={styles.cardButtonText}
            title="出発する?"
            onPress={() => navigation.navigate('Contents')}
          />
        </Card>
      </ScrollView>
    </SafeAreaView>
  );
};

const Stack = createStackNavigator();

const App = () => {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Contents" component={contentsSelect} />
      </Stack.Navigator>
    </NavigationContainer>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  headerImg: {
    width: 90,
    height: 30,
    top: 60,
  },
  hearderMessage: {
    marginTop: 100,
    textAlign: 'center',
  },
  hearderMessageBody: {
    flex: 1,
    top: 20,
    marginBottom: 60,
    textAlign: 'center',
    fontWeight: 'bold',
    color: '#93a5b1',
  },
  cardImage: {
    width: 350,
    height: 350,
  },
  cardText: {
    marginBottom: 10,
  },
  cardButtonText: {
    marginTop: 20,
    marginBottom: 20,
  },
});

export default App;

header component

名前から察するようにヘッダーについてまとめたファイルになります。

header.js
import React from 'react';
import {View, StyleSheet, Image} from 'react-native';

const AppHeader = () => {
  return (
    <View>
      <Image
        source={require('../../public/img/Header.png')}
        style={styles.headerImg}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  headerImg: {
    width: 90,
    height: 30,
    marginLeft: 20,
    top: 30,
  },
});

export default AppHeader;

contents component

こちらのcontents componentは、ユーザが実際に何のコンテンツについて学びたいかを選ばせるページの処理をまとめています。

contentsSelect.js
import React from 'react';
import {SafeAreaView, ScrollView, StyleSheet} from 'react-native';
import {Card, Title} from 'react-native-paper';
import AppHeader from './header';

const contentsSelect = () => {
  return (
    <SafeAreaView style={styles.container}>
      <ScrollView>
        <AppHeader />
        <Card style={styles.cardPadding}>
          <Card.Content>
            <Title style={styles.cardTitle}>宇宙って</Title>
            <Card.Cover
              source={require('../../public/img/alien.png')}
              style={styles.cardImg}
            />
          </Card.Content>
        </Card>
        <Card style={styles.cardPadding}>
          <Card.Content>
            <Title style={styles.cardTitle}>ALSって知ってる</Title>
            <Card.Cover
              source={require('../../public/img/health.png')}
              style={styles.cardImg}
            />
          </Card.Content>
        </Card>
        <Card style={styles.cardPadding}>
          <Card.Content>
            <Title style={styles.cardTitle}>Falcon 9がすごい</Title>
            <Card.Cover
              source={require('../../public/img/startup_isometric.png')}
              style={styles.cardImg}
            />
          </Card.Content>
        </Card>
      </ScrollView>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  cardImg: {
    height: 300,
  },
  cardPadding: {
    marginBottom: 20,
  },
  cardTitle: {
    fontWeight: 'bold',
  },
});

export default contentsSelect;

動作

ezgif.com-gif-maker.gif

次回

次回は、コンテンツ選択した後の詳細画面を作り込んでいこうと思います。

おわり

かなり気合を入れて作成を始めたので、随時記事も更新していこうと思います。
また、React-vativeをさわってみたい方に対し、開発コードがお役に立てば幸いです。

参考

React-native
React-native-elements
React Navigation
React-native-paper
manypixels
難病情報センター: ALS

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

textareaを使わずに複数行文字列入力するのを作ってみた

Twitterのツイート入力するところを眺めて気がついてちょっと驚いたのでメモ程度に
作ったというのもおこがましいレベルの代物ですが…w

縦方向に勝手に伸びるのでフォントサイズとか深く気にしなくていいのは便利ですね

やり方

  • 超端的に書くとこれだけで出来ます
    • ただこのままだとめちゃくちゃ見づらい上に入力しづらいので、適当にCSS当てたほうがいいです。
<span contenteditable="true"></span>

デモ

備考

  • 入力値が HTML になるので何かしらのハンドリングをしないと扱いづらいと思います
    • 因みに文字選択状態で Ctrl + B とか、 Ctrl + I とかすると装飾がかかりますが、環境によっては多分うまく動きません
  • 気になって調べたら TinyMCE はこの方式で実装されてるみたい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む