最近Expoでアプリ書いてるのに使ったTipsなど

React Native Advent Calendar 2018 - 17日目の記事です

最近Expoでアプリを書いているので、そこで使った小ネタを紹介

画面全体を覆うスタイルを定義したい時

Stylesheet.absoluteFillObject

画面全体をピッタリ覆うスタイルを付けたい時に使えます

export default class App extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <TouchableOpacity>
          <Text>Button</Text>
        </TouchableOpacity>
        <View style={styles.fill} />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  },
  fill: {
    // 縦横いっぱいでabsoluteなViewのスタイルを設定
    ...StyleSheet.absoluteFillObject,
    // わかりやすく色を付けてる
    backgroundColor: 'rgba(255, 0, 0, 0.2)'
  }
});

f:id:rskull:20181216205956p:plain

赤くなってる範囲が <View />absoluteFillObject を設定した範囲です

いつ使うのか? 例えば環境に影響されない(どこに置いても成り立つ)フローティングボタンを作りたい場合などに使えます

f:id:rskull:20181216210052p:plain

被さったViewのタッチ判定を無効化する

当たり前ですが、このままだと赤いマスクの裏の画面が全く触れなくなってしまいます

そんな時は <View />pointerEvents="box-none" を設定します

      <View style={styles.fill} pointerEvents="box-none">
        // ...
      </View>

全面にある赤い <View /> のタップ判定を無効にして、なにもないように振る舞わせることができます

f:id:rskull:20181216205853g:plain

意外と色んなところで使えそう

キーボードの上に「完了」とか付けるやつの実装

これ、よく使うと思います

f:id:rskull:20181216211520p:plain

RNにもともと入ってる InputAccessoryView コンポーネントで実装はできますが、バギーで使えませんでした

自分で実装したんですが、ここでも pointerEvents="box-none" が出てきます

スペーサーに外部のライブラリ使ってますが

import React from 'react';
import { View, ViewProps, StyleSheet } from 'react-native'
import KeyboardSpacer from 'react-native-keyboard-spacer';

interface Props extends ViewProps {
  children: React.ReactNode;
}

export const InputAccessoryView = ({ children, ...rest }: Props) => (
  <View {...rest} pointerEvents="box-none" style={styles.wrap}>
    {children}
    <KeyboardSpacer />
  </View>
)

const styles = StyleSheet.create({
  wrap: {
    ...StyleSheet.absoluteFillObject,
    justifyContent: 'flex-end',
  }
})

children がキーボードの上に乗るところです

あとはこのコンポーネントを使って好きに実装します

import React from 'react';
import { View, ViewProps, TouchableOpacity, Text, Keyboard } from 'react-native'
import { InputAccessoryView} from './InputAccessoryView'
import styled from 'styled-components'

const Inner = styled(View)`
  align-items: flex-end;
  padding: 12px 16px;
  background: #f2f2f2;
`;

const TextButton = styled(TouchableOpacity)`
  font-weight: bold;
  color: #15a2ff;
`;

interface Props extends ViewProps {
  visible?: boolean;
  onClosed?: () => void
}

export const InputAccessory = ({ visible, onClosed, ...rest }: Props) => {
  if (visible === false) {
    return null
  }

  return (
    <InputAccessoryView {...rest}>
      <Inner>
        <TextButton
          onPress={() => {
            if (onClosed) {
              onClosed()
            }
            Keyboard.dismiss()
          }}
        >
          <Text>完了<Text>
        </TextButton>
      </Inner> 
    </InputAccessoryView>
  )
}

Accessory.defaultProps = {
  visible: true,
  onClosed: undefined,
}

例えばこんな感じ

「完了」ボタンを置いてキーボードが閉じるように実装します

<InputAccessoryView /> はただキーボードの上に乗る要素を作るだけのコンポーネントです

f:id:rskull:20181216212906g:plain

https://gist.github.com/rskull/3fb91f4c19f49d86355987d1c35fbb35

まとめ

かなりざっとな記事ですみません

3ヶ月くらいExpoで開発してる感想は、めっちゃ楽の一言でした

以前素のRNを2年半くらい触ってましたが、ビルドは落ちるし時間かかるしで辛かったです(当方ネイティブ初心者なので...)

プロジェクトごとにアカウントを作って、そのアカウントをメンバーで共有すれば

コマンドでデプロイ後すぐにシェア、確認できるのですごい作業がはかどりました

なんとかリリースまでこぎつけたいですね

以上です。良いお年を

Typescript + React + Redux + SSR 辺りの素振り

仕事で使うことになりそうなので素振り

要点を抑えながらシンプルめに書いてみた

インクリメントするだけのアプリ

セットアップ

基本的なの

$ yarn add react react-dom redux react-redux
$ yarn add --dev typescript ts-loader webpack webpack-cli

型情報も入れる

$ yarn add --dev @types/react @types/react-dom @types/react-redux

サーバー

$ yarn add fastify serve-static
$ yarn add --dev @types/serve-static

ディレクトリ構成

SSRするのでクライアントとサーバー側それぞれでエンドポイントを用意してる

├── app // 共通のコード
├── client
│   └── index.tsx
├── server
│   ├── Html.tsx
│   └── index.tsx
├── tsconfig.client.json
├── tsconfig.json
├── webpack.config.js
└── package.json

最終的なコード

github.com

yarn start するとサーバーが立ち上がって、初回のSSRだけされるようになってる

f:id:rskull:20180827014625g:plain

参考にした記事

ReactNative アップグレード対応 v0.52 -> v0.56

みんな大好き恒例のアップグレードをしたので、その時にハマった事のメモ

今回は v0.52.2 から v0.56.0 にアップグレードした

前回のアップグレードよりは楽だったけど、本体のバグらしき罠にだいぶはめられた

ちなみに前回と同じく $ react-native-git-upgrade は使わずに

$ react-native init myApp で新たに作成したフォルダに自力で差分をみてマージをしてる

なんだかんだこの方法が一番キレイに出来る

差分の確認

念の為、今回のアップデートの差分を目視確認しておく

自分の環境に合わせて差分が見れるので、それを適用すればOK

Comparing rn-0.52.2...rn-0.56.0 · ncuillery/rn-diff · GitHub

AndroidcompileSdkVersion などが上がった対応

おそらくこれが今回の一番大きな変更

  • compileSdkVersion 23 -> 26
  • buildToolsVersion 23.0.1 -> 26.0.3
  • targetSdkVersion 22 -> 26

これにより、依存してるサードパーティが大体 23 のため、警告が出たりビルドが落ちたりする

正しい対策じゃないかもしれないけど

android/build.gradle に以下のコードを追加して対応した

subprojects {
    afterEvaluate {project ->
        if (project.hasProperty("android")) {
            android {
                compileSdkVersion 26
                buildToolsVersion '26.0.3'
            }
        }
    }
}

警告出されたけどビルドは通るようになった

※ 正しい方法があったら教えて下さい!

リリースビルドのアプリを起動すると直後にクラッシュする

こんなエラーが出てた

undefined is not an object (evaluating 'r.default')

結果的にいうと、原因は react-navigaiton だった

PRは却下されてるけど、とりあえずこの修正を適用すると動くようになる

Fix issue undefined is not an object evaluating r.default by ebaynaud · Pull Request #4699 · react-navigation/react-navigation · GitHub

import が正しく出来ないバグっぽいので、根本的な理由は多分 babel7 に移行したことで何か問題が起きてるのかな??

flowのエラー

型が強化されたみたいで、エラーがたくさん出た

気合で直す

CocoaPodsの修正

BatchedBridge がなくなって、代わりに CxxBridge を追加した

   pod 'React', :subspecs => [
      // ...
+    'CxxBridge',
-    'BatchedBridge'
   ], :path => '../node_modules/react-native'

正直このへんよくわからないし触りたくない

Jestが実行できない

Jestを実行すると、そもそも動かなくていろんなエラーがでて落ちる

Issueはこれ

github.com

package.json に以下のコードを追加したら動くようになった

  "jest": {
    "preset": "react-native",
     "transform": {
       "^.+\\.js$": "<rootDir>/node_modules/react-native/jest/preprocessor.js"
     }
  }

これはとりあえずの回避策で

PRはちゃんと出てるみたい: Fix jest-preset to use internal preprocessor (fix testing with 0.56) by vovkasm · Pull Request #20068 · facebook/react-native · GitHub

動くようになった

自分がハマったのはこれくらいです

--

※ 2018/07/25 追記

TextInputの日本語入力が出来ない件

すっかり忘れてたけど、直ってないじゃん!!

今の所、これが最善っぽい?

gist.github.com

元Issue: [iOS][TextInput] Fix controlled TextInput for Chinese (and other languages) by magicien · Pull Request #18456 · facebook/react-native · GitHub

TwitterKitを使ってるアプリでWeb経由の認証が出来なくなったのを解決した

Twitterの仕様変更

本日(2018/06/12)に認証周りの仕様が変わった。

認証して戻ってくる時のコールバックURLをデベロッパーの管理画面で設定するのが必須になった

Callback URL — Twitter Developers

TwitterKitを使ってログインを実装してるアプリで、端末にTwitterが入ってなければブラウザが立ち上がって認証が行われる

その場合、コールバックURLの設定をしてないと認証がコケるようになってしまった

ちなみにReactNativeアプリの開発でTwtiterの認証にreact-native-twitter-signinを使っている

github.com

修正

アプリの管理画面の設定を開く

https://apps.twitter.com/

f:id:rskull:20180612192226p:plain

コールバックURLに

twitterkit-YourConsumerKey:// の形式で登録する

これはTwitterKitがコールバックURLとして使っているもの

YourConsumerKeyは自分のアプリのコンシューマーキー

://を末尾に付けないと登録出来ないので注意

f:id:rskull:20180613123301p:plain

..

f:id:rskull:20180612191828p:plain

登録が終わったら、コールバックをロックしても正常に認証出来るようになった

※ 追記(2018/06/14)

上記の対応だと、AndroidのWeb経由だけ認証が通らなかった

合わせて下記の対応でiOSTwitter経由/Web経由)AndroidTwitter経由/Web経由)すべての経路で認証が通るようになった

ちなみにtwittersdk://の対応だけだとiOSのWeb経由が通らなくなった(僕の環境だと)

この辺の挙動アナウンスないしほんと謎