そーす

I'm a programmer in Fukuoka. Please contact me saubre.app[at]gmail.com or Twitter DM.

React Native Web から始める React Native

[追記]SpotifyじゃなくてShopify

engineering.shopify.com

ShopifyがネイティブからReactNativeへ移行するとのことで界隈が盛り上がってます。

ここまでの企業がネイティブアプリをReactNativeに作り変えるのは初めてじゃないでしょうか。

移行の理由としては

  • ReactNative強強企業を買収して検証とかしてたっぽい
  • WebとアプリでReactのノウハウを共有できる強み
  • パフォーマンスも上がってきてる

というのを上げてました。

1つ目はShopifyだけの理由ですが、他2つについては同じ意見ですね。やはりWebやってもアプリやってもReact(JS/TSも)のノウハウが生きるのはホント強い…

そしてもう1つ、ブログで述べられていたのが

「At Shopify we’ll be doing some React Native Web experiments in 2020.」

React Native Web も実験的に使っていくとのこと。

あと面白かったのは

  • iOSアプリのクラシュが減った

ってところですね。やはりUIの状態管理の難しさがクラッシュに繋がることが多いようですね。

ということでいきなりReact Nativeはキツい人向けのReact Native Webで始めるReact Native入門です。

入門…というかreact-native-webの誤解を解く

react-native-webってreact-nativeをWebで動かすなんか凄いFWみたいな勘違いをされている人を結構見かけます。

コレ全然そんな事無くて、ただのWeb用のReactのコンポーネントライブラリなんですよね。

なのでReactNative知らなくてもReactがわかれば使えます。

yarn add react-native-web

import React from "react";
import { View, Text } from "react-native-web";

export default () => (
  <View>
    <Text>Hello React Native Web</Text>
  </View>
);

f:id:saburesan:20200130125549p:plain

何がReactNativeなのかというと、ライブラリのインポートの仕方とかコンポーネントのスタイル、Propsの定義などが同じです(一部違いますが…)

React Nativeだとインポート先が 'react-native-web' から 'react-native' になるだけです。

なので、React Native が分かる人がWebをやる場合はReact Native Webを使うとすんなりWebアプリが実装出来ます。

逆にWebしかわからない人がReact Native Webに慣れているとReact Nativeでアプリの実装出来ます。(もちろんUI部分だけの話です)

そもそもReactを使う時点でノウハウの共有ができるのに加えて、React Native/React Native Webを使うことでコンポーネント自体も共有することができます。

仮にWeb ReactしかやらないエンジニアとReact Nativeしかやらないエンジニアがいたとしても、Web ReactのエンジニアにReact Native Webを使ってなれてもらえばReact NativeのUI部分もある程度任せることができます。

なかなかWebアプリエンジニアとネイティブアプリエンジニアが設計で共通の話題をするのは難しいように思いますが、React Native/React Native Web を使うことでUIや状態管理の部分でWebエンジニアとアプリエンジニアが設計やノウハウを共有できるというのは非常に強いと思います。

むしろ僕なんかはWebもアプリもReact, Next.js, React Nativeで作るのでUI部分に関しては変にコンテキストスイッチしなくていいのがめちゃくちゃ良いです。

ちなみに、styled-componentsもreact-native対応していて

import styled from 'styled-components/native'

const Wrapper = styled.View`
  flex: 1;
`

みたいな感じで使えます。

Web Reactエンジニアが始めるなら

1.React Native Web

「WebのReactは書けるけどネイティブアプリ全然わかない」って人はまず React Native Webを使ってWebのアプリケーションを作るとことから始めると良いでしょう。

そこでReact Native Webのコンポーネントの使い方を学べばReact NativeでもすんなりUIが組めるようになるはずです。

2. Expo

次に、いきなり素のReact Nativeに行くのではなく、Expoを使いましょう。

expo.io

Expoは簡単にいうと素のReact Nativeで触る必要のあるネイティブ部分をすべてラップしてJSだけでアプリが完結できるようにしてくれるフレームワークです。

これだと、WebのReactがわかっていてReact Native Webがわかっていればアプリが組めるようになるはずです。素のReactNativeでは入れるのが面倒な便利機能がたくさんあるので実際に動かして見てほしいです。

ちなみにExpoならWindowsパソコンでiOSアプリが開発出来ます。実機があればデバッグ・テストしながらも開発出来ます。便利ですね。

Web上でコードを試し書きできる snack.expoというサービスもあります。とりあえずこれで遊んでみるといいですね。

snack.expo.io

3. 素のReact Native

Expoの欠点はネイティブの機能を自分で追加できないという所です。おそらく仕様の決定権を持っていないと、多くの場合はExpoでカバー出来ない仕様が実装必要になると思います。

そうなってくると素のReact Nativeを使うしかありません。

iOSアプリを作るためにMacが必要ですし、iOS/Androidアプリのエンジニアが必要になってきます。

Webエンジニア目線で行くと素のReact Nativeを触るのはExpoからの差を考えるととんでもなくハードルが高いです。

その場合はかならずネイティブアプリ のエンジニアに相談できる環境を整えましょう。

まとめ

Web Rectエンジニアもアプリ触るの億劫だなーと思う人ると思うのですが、react-native-webやってExpoやって〜ってしていくと案外すんなりアプリが作れる様になると思うので 、ぜひ試してほしいです。

Redux, ReduxThunkを使った時のActionの型導出

Redux, Redux-Thunkを使った時のActionの型導出 目的は

 const reducer = (state: State = initial, action: Action): State => {
 ...
 }

の第2引数のActionの型をできるだけ簡単に導出することです。 具体的にはDucksパターンを用いた設計でActionCreatorからActionTypeやActionの型を別途定義することなく実際のActionCreatorの定義からActionを導出するというものです。 別途定義するというのは Usage With TypeScript · Redux ここに書いてあるようなActionTypeやActionの型を定義することをさします。

通常のReduxで使う同期ActionとReduxThunkで使う非同期Actionの2つに分けて導出していきます。

同期Action

 const setUser = (user: User) => ({ type: 'SET_USER', payload: { user }})
 const deleteUser = () => ({ type: 'DELETE_USER' })
 
 // setUserの型は以下の用に推論されている
 const setUser: (user: User) => {
     type: string;
     payload: {
         user: User;
     };
 }

同期Actionというのはこういうやつですね。多分よくあるActionを返すActionCreatorの定義だと思います。これだとtypeはstringならなんでもいいという型になってしまっています。 まずこれのtypeを定数扱いにするために as const をtypeに付けます。

 const setUser = (user: User) => ({ type: 'SET_USER' as const, payload: { user }})
 const deleteUser = () => ({ type: 'DELETE_USER' as const })

次にこれらのそれぞれのActionCreatorの型を導出します。typeofというキーワードで型を導出できるのでそれを用います。

 const setUser = (user: User) => ({ type: 'SET_USER', payload: { user }})
 const deleteUser = () => ({ type: 'DELETE_USER' })
 
 type SetUserType = typeof setUser
 type DeleteUserType = typeof deleteUser

こうすると例えばSetUserTypeは

 type SetUserType = (user: User) => {
     type: 'SET_USER';
     payload: {
         user: User;
     };
 }

このような型として導出されます。 次にこれはActionを作る関数の型なのでActionの型をこれから導出します。ReturnTypeというGenericsの型があるのでそれを使います。これは関数の型定義からReturnされる型を導出してくれます。

 type SetUserType = typeof setUser
 type DeleteUserType = typeof deleteUser
 
 type SetUserActionType = ReturnType<SetUserType> 
 type DeleteUserActionType = ReturnType<DeleteUserType>

これでSetUserActionTypeは

 type SetUserActionType = {
     type: 'SET_USER';
     payload: {
         user: User;
     };
 }

という型として導出されます。 これを1つにまとめるとすると

type Action = SetUserActionType | DeleteUserActionType

この型は

 type Action = {
     type: "DELETE_USER";
 } | {
     type: "SET_USER";
     payload: {
         user: User;
     };
 }

こういう型として導出されます。 毎回これ全部書くのはもちろん面倒なので

 type Action = ReturnType<typeof setUser | typeof deleteUser> 

これで済みます。 ここまででとりあえずActionの型は導出できましたが、この方法だとActionCreatorが増えるたびにActionの型にも手を加える必要があります。ActionCreatorに追加されたらActionも自動で導出されるようになるのが理想ですね。 まずはそれぞれのActionCretorを1つのオブジェクトとして定義します。

 const ActionCreator = {
    setUser: (user: User) => ({ type: 'SET_USER' as const, payload: { user }}),
    deleteUser: () => ({ type: 'DELETE_USER' as const })
 }

このActionCretorからsetUserの型を導出するには

 type SetUserType = typeof ActionCreator['setUser']

このように書けます。 すべてのActionCreatorのKeyに対する型の導出はkeyofを使って

 type ActionCreatorType = typeof ActionCreator[keyof typeof ActionCreator]

と書けます。 あとはこれを先程の定義と混ぜると

 type Action = ReturnType<typeof ActionCreator[keyof typeof ActionCreator]>

こうすることでActionCreatorに追加するとActionの型定義がちゃんと導出されます。

 const ActionCreator = {
    setUser: (user: User) => ({ type: 'SET_USER' as const, payload: { user }}),
    deleteUser: () => ({ type: 'DELETE_USER' as const }),
    setHoge: (fuga: string) => ({ type: "SET_HOGE" as const, payload: { fuga }}) // 新しく追加
  }
  
  // ActionCreatorにsetHogeを追加するだけでActionには定義がちゃんと導出される
  type Action = ReturnType<typeof ActionCreator[keyof typeof ActionCreator]>

非同期Action

ここではRedux-Thunkを使った非同期処理時の型導出を行います。 Redux-Thunkを使うと関数をdispatchの引数として渡すことができます。その関数の中でdispatchされるアクションの導出が目的となります。今回は例としてユーザ情報を取得するfetchUserという非同期Actionを考えます。

 const fetchUser = (id: string) => async (dispatch, getState) => {
    dispatch({ type: 'FETCH_USER#START' })
    try {
        const { data } = await axios.get<User>(`/users/${id}`)
            dispatch({ type: 'FETCH_USER#SUCCESS', result: { user: data } })
    } catch(e) {
        dispatch({ type: 'FETCH_USER#FAILE', error: { errroMessage: "Error" } })
    }
 }

dipatchされるActionは3つです。

  • { type: 'FETCH_USER#START' } 非同期アクションの開始
  • { type: 'FETCH_USER#SUCCESS', result: { user: data } } 非同期アクション成功
  • { type: 'FETCH_USER#FAILE', error: { errroMessage: "Error" } } 非同期アクション失敗

ここで、ReduxThunkに定義されているThunkActionという非同期アクションの型定義を使います。

 export type ThunkAction<R, S, E, A extends Action> = (
   dispatch: ThunkDispatch<S, E, A>,
   getState: () => S,
   extraArgument: E
 ) => R;
 
 export interface ThunkDispatch<S, E, A extends Action> {
   <T extends A>(action: T): T;
   <R>(asyncAction: ThunkAction<R, S, E, A>): R;
 }

これを使うと先程のfetchUserは以下のように書けます。

 // reduxのstoreの定義がStoresで、extraArgumentが無いと仮定
 const fetchUser = (id: string): ThunkAction<void, Stores, {}, 
  { type: 'FETCH_USER#START' } | 
  { type: 'FETCH_USER#SUCCESS', result: { user: User } } |
  { type: 'FETCH_USER#FAILE', error: { errroMessage: string } }
 > => async (dispatch, getState) => {
    // ThunkActionの第4引数の定義のおかげでdispatchするActionにもチェックが入ります。
    dispatch({ type: 'FETCH_USER#START' })
    try {
        const { data } = await axios.get<User>(`/users/${id}`)
        dispatch({ type: 'FETCH_USER#SUCCESS', result: { user: data } })
    } catch(e) {
        dispatch({ type: 'FETCH_USER#FAILE', error: { errroMessage: "Error" } })
    }
  }

次にこの定義からdispatchされる型を導出します。これを導出するにはinferというキーワードを使って型をキャプチャします。

 type AsyncActionType<T> = T extends ThunkAction<any, any, any, infer R> ? R : never

これはAsyncActionTypeのGenerics TがThunkActionを継承している場合、ThunkActionの第4引数の型をinferによってRでキャプチャします。これによりキャプチャされたRがAsyncActionTypeになるという定義です。これを使うとfetchUserがdispatchするActionは以下のように導出できます。

 type FetchUserActionTypes = AsyncActionType<typeof ReturnType<typeof fetchUser>>
 
 // 導出結果
 type FetchUserActionTypes = {
     type: "FETCH_USER#START";
 } | {
     type: "FETCH_USER#SUCCESS";
     result: {
         user: User;
     };
 } | {
     type: "FETCH_USER#FAILE";
     error: {
         errroMessage: string;
     };
 }

あとは同期Actionと同じようにAsyncActionCreatorというオブジェクトを作ってAsyncActionCreatorに追加するだけで自動で導出がされるようにします。

 const AsyncActionCreator = {
    fetchUser: (id: string): ThunkAction<void, Stores, {}, 
      { type: 'FETCH_USER#START' } | 
      { type: 'FETCH_USER#SUCCESS', result: { user: User } } |
      { type: 'FETCH_USER#FAILE', error: { errroMessage: string } }
     > => async (dispatch, getState) => {
        // ThunkActionの第4引数の定義のおかげでdispatchするActionにもチェックが入ります。
        dispatch({ type: 'FETCH_USER#START' })
        try {
            const { data } = await axios.get<User>(`/users/${id}`)
            dispatch({ type: 'FETCH_USER#SUCCESS', result: { user: data } })
        } catch(e) {
            dispatch({ type: 'FETCH_USER#FAILE', error: { errroMessage: "Error" } })
        }
      }
 }
 type AsyncActionType<T> = T extends ThunkAction<any, any, any, infer R> ? R : never
 type Action = AsyncActionType<ReturnType<typeof AsyncActionCreator[keyof typeof AsyncActionCreator]>>

まとめ

最後に同期Actionと非同期Actionをまとめると

 const ActionCreator = {
   setUser: (user: User) => ({ type: 'SET_USER' as const, payload: { user } }),
   deleteUser: () => ({ type: 'DELETE_USER' as const }),
   setHoge: (fuga: string) => ({ type: 'SET_HOGE' as const, payload: { fuga } }) // 新しく追加
 }
 
 const AsyncActionCreator = {
   fetchUser: (
     id: string
   ): ThunkAction<
     void,
     Stores,
     {},
     | { type: 'FETCH_USER#START' }
     | { type: 'FETCH_USER#SUCCESS'; result: { user: User } }
     | { type: 'FETCH_USER#FAILE'; error: { errroMessage: string } }
   > => async (dispatch, getState) => {
     dispatch({ type: 'FETCH_USER#START' })
     try {
       const { data } = await axios.get<User>(`/users/${id}`)
       dispatch({ type: 'FETCH_USER#SUCCESS', result: { user: data } })
     } catch (e) {
       dispatch({ type: 'FETCH_USER#FAILE', error: { errroMessage: 'Error' } })
     }
   }
 }
 type AsyncActionType<T> = T extends ThunkAction<any, any, any, infer R> ? R : never
 
 type Action =
   | ReturnType<typeof ActionCreator[keyof typeof ActionCreator]> // 同期Action
   | AsyncActionType<ReturnType<typeof AsyncActionCreator[keyof typeof AsyncActionCreator]>> // 非同期Action

福岡ゆるっとIT交流会 vol.3「モバイルアプリを作っている人の話を聞く会」

yurutto-it.connpass.com

「ReactNative枠よろしく!」

と言われたのででます。

珍しくアプリの話する会なので仲間を探してきます。

それに向けてFlutterもいじくり回してる途中。

「A Tour of the Dart Language」をやりました

www.dartlang.org

Flutterのβ版発表やDart2の発表で話題のDart

勉強したことがなかったのですが、公式のドキュメンが結構しっかりしており、その中の「A Tour of the Dart Language」という基礎を学ぶチュートリアルがあったのでやってみました。

www.dartlang.org

また、「DartPad」というオンラインでDartのコードが書けるサービスもあるのでチュートリアル見ながらDartPadで動かして確認という形で進めました。

ちなみにこの以下はDartの説明では無くただの感想です。

Dartのクラス定義なんかは

class MySample {
  String name;
  
  MySample(String name){
    this.name = name;
  }
}

void main() {
  MySample sample = new MySample('Ryohlan');
  print(sample.name);
}

このように書くことができます。

このコードはDartでもJavaでも動くコードですね。

ただ、Dartだと以下の方が推奨されています。

class MySample {
  String name;
  
  MySample(this.name);
}

この推奨される書き方はDartが公式で「Effective Dart」という形である程度のガイドラインを敷いています。これはチーム内での記述を揃えたりコードレビューするときなどに非常に有用です。

www.dartlang.org

さらに、ガイドラインに即したコードフォーマッターも用意してあります。

www.dartlang.org

このあたりがしっかりしていると決め事が少なくなるし「公式がとりあえず提供しているから」という文句である程度収まりがつきます。

構文はJavaに似ている所があり、Javaをやったことがあるプログラマならすんなりと書くことができそうな印象でした。

とはいえ、関数がファーストクラスオブジェクトであったり型推論があったりNull安全な機能があったりAsyncAwait、Generator…とだいぶ提供している機能が違います。

ただ言えるのはいわゆるモダンな機能を実装している言語である、ということでしょうか。

Dartを触ろうと思ったきっかけは、Flutterのβ版が出たこととDart2が出たからです。

Flutterは1年くらい前のα版のときから知ってはいたのですが、正直TypeScriptがGoogleの社内標準言語として発表されたり、

www.publickey1.jp

KotlinがAndroidの開発言語として発表されたりと

www.publickey1.jp

していたのであまり期待はしていませんでした。(ごめんなさい

そんな中Flutterのβ版とDart2の発表だったのでGoogleDart, Flutterに本気なのかなと思い直し、実際Dartを触ってみると個人的には非常に書きやすく読みやすそうだなと思いました(プロダクションで使ってみないとわかんないですが…)。

一応Dart自体はマルチプラットフォーム対応で「Dart for Web」や「Dart VM」などありますが、

まずはFlutterでアプリを作ってみてもう少しちゃんした手応えを感じたいと思います。

FukuokaJSにてReact Native ExpoについてLTしてきました

www.slideshare.net

FukuokaJSというイベントでLTしてきました

fukuokajs.connpass.com

やっぱ福岡だとReactもReactNativeも全然やってるひといなくて非常に疎外感がありますが、頑張っております。

ネタとしてはいくつか考えていたのですが、

そもそもReactの利用者が少ないっぽいのでNextJSとかReactとかReactNativeの話しても微妙っぽいと思って

「ネイティブ触らなくてもJSでネイティブアプリ作れるExpo」

みたいな雑なLTになりました。

今更Expoかよ、みたいな感じなんですがやっぱり知らない人とか結構いたようでLTしてよかったなーと思いました。

しっかし福岡Vue強いなーーーーーーー

オブジェクトの配列の重複削除

const merge = (newList, oldList, key) =>
  Object.values([...oldList, ...newList].reduce((a, b) => {
    a[b[key]] = b;
    return a;
  }, {}));

const l1 = [
{ id: '1', value: 'l1v1' },
{ id: '2', value: 'l1v2' },
{ id: '3', value: 'l1v3' },
];

const l2 = [
{ id: '2', value: 'l2v1' },
{ id: '4', value: 'l2v2' },
{ id: '5', value: 'l2v3' },
]

console.log(merge(l1, l2, 'id'));
0: {id: "1", value: "l1v1"}
1: {id: "2", value: "l1v2"}
2: {id: "3", value: "l1v3"}
3: {id: "4", value: "l2v2"}
4: {id: "5", value: "l2v3"}

重複があった場合は第一引数(new)の方を残す感じ。重複チェックしたいオブジェクトのキーをkeyに指定。

なんか他にいい方法ありますかね。

Proton-native

Proton Native - React Native for the desktop, cross compatible

https://proton-native.js.org/SVG/Artboard%201.svg

Reactを使ってデスクトップアプリが作れるライブラリが公開されていました。

なんでも

Create native desktop applications through a React syntax, on all platforms Same syntax as React Native

だそうです。

libuiというマルチプラットフォームGUIライブラリがあるのですがそれをnodeから使えるようにするlibui-nodeというライブラリがあって、Proton-nativeではProton-nativeのオーナーがlibui-nodeをフォークしているものを取り込んでいるようです。

このあたりはReact Nativeっぽさがありますね。

Quick Start

Proton Native - React Native for the desktop, cross compatible

してみました。

f:id:saburesan:20180216075211p:plain

これだと面白くないので、リポジトリに含まれてたDemo.jsの中身をコピってみます。

https://gyazo.com/d6ce18deadab61313bf91b809744a59e

色々試してみた

import React, { Component } from 'react';

import { render, Window, App, TextInput, Dialog, Box, Button, Checkbox, Picker, ProgressBar, Text } from 'proton-native';

class Example extends Component {
  state = {
    formText: '',
  }

  render() {
    return (
      <App>
        <Window title="Example" height={500} width={500}>
          <Box>
            <TextInput stretchy={false} onChanged={(formText) => this.setState({ formText })}/>
            <Text>{this.state.formText}</Text>
            <Button onClicked={() => Dialog('Error', {title: "Button Clicked"})}>Button</Button>
            <Box padded height={100} vertical={false}>
              <TextInput stretchy={false} onChanged={(text) => Dialog('Error', {title: text})}/>
              <Button onClicked={() => Dialog('Error', {title: "Button Clicked"})}>Button</Button>
            </Box>
            <Checkbox>This is a checkbox</Checkbox>
            <Picker>
              <Picker.Item>Option 1</Picker.Item>
              <Picker.Item>Option 2</Picker.Item>
              <Picker.Item>Option 3</Picker.Item>
            </Picker>
            <ProgressBar value={-1} />
          </Box>
        </Window>
      </App>
    );
  }
}

render(<Example />);

https://gyazo.com/3858156a592e22b9e5772c6e55c0a74e

まとめ

React/ReactNativeのようなstyleは今のところ変更できないようです。

今後に期待ですね。

アウトプットして起きたこと

ブログやらLTやらで少しづつアウトプット出してて身に起きたことです。

名前を覚えられる

前にReact Nativeについての発表を何回かしたのですが、同僚がある勉強会(私は行ってないです)に行ったときに名刺交換で

「React Nativeの〇〇さんがいる会社ですね!」

って言われたそうで。嬉しい。

採用の応募がある

React, React Native, Kotlinなど流行りのものをやっていると「福岡 React Native」とかで引っかかるんですかね。

福岡で働きたいって人からちょいちょい採用の連絡が来ます。

外国人が多いですね。

弊社、インドネシア向けの事業しかやってないので今のところ基本的に外国人採用が多いです。

応募お待ちしております!

仕事の依頼がある

これも国内外関わらず来ます。

海外から仕事の依頼があったのはびっくりしましたね。

相手は日本語話せない感じですが、

「ブログとかGithub見たぜ〜、うちのチームに入らないか?」

ってくる感じ、これがインターネッツ!!

まとめ

アウトプットすると何故か海外とのつながりができる。

プログラミングってすごい。

無印のクラックプレッツェルチョコレートが美味い

www.muji.net

これがマジで美味い。

美味すぎる。

今日大名店のやつ買い占めてきた。

2つ以上で10%OFFだったので。

1つ100円なんで買ってみてください。

Kotlinでa == 1 && a == 2 && a == 3

前回の記事はJavaScripta == 1 && a == 2 && a == 3が成り立つにはどうすれば良いか、という内容でしたが元記事のブコメ

プロパティある言語(C#, Swiftなど)ならトリッキーなことしなくても普通にできちゃう

とりあえずKotlinのDelegation。

一番楽ですぐ思いつく方法はコレじゃないでしょうか

import kotlin.reflect.KProperty

class Delegate {
    var count = 0

    operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
        count++
        return count
    }
}

fun main(args: Array<String>) {
    val a by Delegate()
    if ( a == 1 && a == 2 && a == 3) {
        println("true")
    }
}

JavaScriptの比較演算子の話

stackoverflow.com

これ、よく思いつくなーと感心しました。

普段等価演算==使うことないのですが、==の場合だと上記の回答のようなことができるようです。

valueOfでも大丈夫だって書いてあったのですが、toStringvalueOfどっちも定義されている場合、toStringが呼ばれることはありませんでした。

% node
> const a = { toString: () => console.log("toString"), valueOf: () => console.log("valueOf") };
undefined
> a
{ toString: [Function: toString], valueOf: [Function: valueOf] }
> a == 1
valueOf
false
> a == "1"
valueOf
false
> a === 1
false
> a === "1"
false
>

valueOfのリファレンスには

developer.mozilla.org

文字列型の文脈にあるオブジェクトは toString メソッドを通じて変換されますが、

とあるんですが、

> console.log(a + " string")
valueOf
undefined string
undefined

文字列の文脈って上記のようなやつは違うんですかね。

Next.jsに動的ルーティングを追加する「next-routes」

Nextjsにはデフォルトでルーティング機能があります。

[root_directory]/pages/ディレクトリ構成がそのままルーティングになります。

しかし、RESTfulなURIにする場合はサーバーの設定をカスタマイズして手動でしなければなりません。

そのような動的なルーティングを追加するためのモジュールがnext-routesです。

github.com

動的なルーティングを追加

たとえば、/users/show?id=ryohlan/users/ryohlanというルーティングを貼りたい場合は

# routes.js
const routes = module.exports = require('next-routes')()

routes
.add('/users/:id', '/users/show')

このように定義して、

# server.js
const next = require('next')
const routes = require('./routes')
const app = next({dev: process.env.NODE_ENV !== 'production'})
const handler = routes.getRequestHandler(app)

const {createServer} = require('http')
app.prepare().then(() => {
  createServer(handler).listen(3000)
})

という風にサーバーの設定を変更することで

/users/ryohlanへのアクセスを/users/show?id=ryohlanで受けることができます。

クリックで遷移の時

Nextjsではクリック遷移の時はNextimport Link from 'next/link'でLinkコンポーネントを使うのですが、

next-routesにはLinkコンポーネントもあるので代用することで動的なルーティングを使うことができる

# routes.js
const routes = module.exports = require('next-routes')()

routes
.add('users_show', '/users/:id', '/users/show') # 第1引き数が/で始まらない場合はルーティングのIDとして使うことができる

# pages/users/show.js
import {Link} from '../routes'

export default () => (
  <div>
    <Link route='users_show' params={{id: 'ryohlan'}}>
      <a>Hello world</a>
    </Link>
  </div>
)

react-native-firebaseのiOSのワークアラウンド

react-nativeでFirebaseを簡単に使えるようになるreact-native-firebaseというライブラリがあります。

rnfirebase.io

ですが、react-native v0.51.0ではiOSの設定がドキュメント通りでは動きません。

そのワークアラウンドについて書いておきます。

RNFirebaseをpodからLibrariesに直接インポートする

ドキュメント通りにreact-native linkをするとPodfile

   pod 'RNFirebase', :path => '../node_modules/react-native-firebase'

が追加されていると思いますが、これを消します。

次に、Finderを開いてnode_modules/react-native-firebase/ios/RNFirebase.xcodeprojXcodeLibrariesにドロップします。

f:id:saburesan:20171213005611p:plain 

f:id:saburesan:20171213005643p:plain

Linked Frameworks and LibrariesにlibRNFirebase.aを追加

f:id:saburesan:20171213010021p:plain

とりあえずこれで動きます。

FirebaseMessagingを追加する時

rnfirebase.io

FirebaseMessagingを追加するステップでAppDelegate.m#import "RNFirebaseMessaging.h"を追加する作業があるのですが、

このヘッダが見つからないのでビルドが通りません。

これはXcodeHeader Search Pathに以下のパスを追加する必要があります。

$(SRCROOT)/../node_modules/react-native-firebase/ios/RNFirebase/messaging

f:id:saburesan:20171213010542p:plain

これでFirebaseMessagingのビルドが通るはずです。