全ての記事

welogger app 開発メモ 2 - react-navigation + redux

2017-11-23 @sunderls

welogger react-native

前の記事(welogger app 開発メモ 1 - react-nativeを試してみた)では、ちょっとだけreact nativeを触ってみた。 いい感じでしたので、週末をかけてもうちょっと進めました。今できてるのは:

  1. tab/stack / modal ページトランジション
  2. redux
  3. real remote API
  4. photo/geo / icon

以下の実際の動くdemo

tab + stack

tabの上にstackを入れ、modalを表示すればokです。

  1. 一番外はstack(modal)
  2. 次は一つのtab
  3. 各tabの下はまたstack(非modal)
// first tab 
const LeftNavigator = StackNavigator({
  List: { screen: History},
  Detail: { screen: Log }
});

// second tab
const MiddleNavigator = StackNavigator({
  Now: { screen: Now},
});

// third tab
const RightNavigator = StackNavigator({
  Cosmos: { screen: Cosmos},
  Shadow: { screen: Shadow},
});

// the tabs
const MainNavigator = TabNavigator({
  Left: { 
    screen: LeftNavigator,
    navigationOptions: {
      tabBarLabel: 'Left',
      tabBarIcon: ({ tintColor }) => (
        <Icon name="md-book" size={30} color={tintColor} />
      ) 
    }
  },
  Middle: { 
    screen: MiddleNavigator,
    ...
  },
  Right: {
    screen: RightNavigator,
    ...
  }
}, {
  initialRouteName: 'Middle',
  animationEnabled: false,
});

// outer stack in modal mode
const AppNavigator = StackNavigator({
  Main: { screen: MainNavigator},
  Login: { screen: Login },
  Edit: { screen: Edit},
  DatePicker: { screen: DatePicker},
}, {
  mode: 'modal',
  headerMode: 'none',
})

redux

weloggerではユーザーの入力が多いので、stateの管理が複雑です。

welogger.comにはreduxを使ってて、そのままactions/reducersをappの方に持ってきました。動く!感動した!

変更する必要があったのはreact-navigation、公式マニュアルあるので、指示の通りにすればok。

ちょっと注意すべきなのは、initStateの処理は以下にした方がいいかも。

const initialState = AppNavigator.router.getStateForAction(NavigationActions.init);

reduxを使うメリットとしては、componentDidFocus hookはもういらない。

expoの試し

ここまで開発してきて、cameraを使うことになるので、npm run ejectしないといけない。 デフォルトのオプションはexpoフレームワークですね、react nativeの上に色々コンポーネントやヘルパーなどを提供してくれるものです。試したら使いやすいです。camera/locationなどすぐできる。

けど最後appstoreに申請出すときはちょっと問題があります、expoにはsegmentを使ってます、そのセイで申請出すとき、「IDFA」を使ってるぞって宣言しないといけない。

でもweloggerでは広告をだす予定はない、さらにsegmentのfree tierは1000ユーザー/月、これはちょっと嫌だな。

expoのdynamic js bundleがいいが、自分でサーバーを立ち上げるのもそんなに難しくはないと思って、expoをやめました!!

expoの除去も簡単です。新規react nativeプロジェクトを一個作って、jsとdependenciesをコピペすればいいです。

camera/photo

カメラについてreact-native-image-pickerを使った。

react-native linkでinstallする。global rrなければ、npm scriptでlocalコマンドを用意しましょう。

新しいlibraryとかは基本的にlinkする必要があります。

カメラについてInfo.plistに権限を宣言しないと行けなくて、自分はxcodeで編集しました。簡単です。

このlibraryの使い方が簡単です、公式サンプルに従えばいいです。

画像のアップロードについて、web環境ではFormDataを利用するのが一般的だが、react nativeでも同じ。

const formData = new FormData();
formData.append('file', fileInput.files[0]);

ただ、react nativeにはinput.filesがない、代わりに{url: 'xxx', name: 'filed name'}の感じで送信すればいい感じに変換してくれます。

// get uri from react-native-image-picker
let source = { uri: response.uri };
const formData = new FormData();
formData.append('file', {
    uri: result.uri,
    type: result.type,
    name: 'file' // this seems the field name
});

これでサーバーサイドの特別対応はいらん。

geo location

expo環境では特別のAPIだが、ビルトインのnavigator.getCurrentPositionでも使えるので、そのまま使いました。

export const getCurrentLocation = () => {
    return new Promise((resolve, reject) => {
        navigator.geolocation.getCurrentPosition((pos) => {
            let coding = 'https://maps.googleapis.com/maps/api/geocode/json?latlng=' + pos.coords.latitude + '%2C' + pos.coords.longitude + '&language=ja';
            fetch(coding).then((result)=>{
                result.json().then((result) => {
                    resolve(result.results[0]);
                });
            });

        }, (error) => {
            alert(error && error.message);
            reject(error);
        });
    });
}

vercor icons

welogger.comではfont-awesomeを使ってますが、appの方ではreact-native-vector-iconsを使っています。

import Icon from 'react-native-vector-icons/Ionicons';
...
<Icon name="md-book" size={30} color={tintColor} />

loading indicatorなら、ビルトインの ActivityIndicatorがいいと思います。

headerのtitleを動的に変更する

react-navigationのsetParamsをcallすればokです。

// sample page
class Page extends React.Component {
  static navigationOptions = ({ navigation, dispatch }) => {
    return {
      title: navigation.state.params.title, // set title from params
      headerRight: <Button title="Edit" onPress={() => {
        // navigate from top-right button
        navigation.navigate('Edit', {
              id: navigation.state.params.id
        });
      }}/>
    };
  };
  ...

  componentWillReceiveProps(nextProps) {
    // if title needs to change, use navigation.setPrams
    // setTimeout is to fix rendering problem. maybe there
    // are better ways.
    if (this.props.log !== nextProps.log) {
      setTimeout(() => {
        this.props.navigation.setParams({
          title: nextProps.log.title
        });
      }, 0);
    }
  }
}

まとめ

ここでアプリのページ遷移もちゃんと動くようになりました。reduxのおかげでstateの管理も楽になったので、これからはUIの改善ですね。

デフォルトのtabがいいが、クールではないので、そこの今後なおして行きます。

では次回お会いしましょう。