全部文章

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. 真实数据API,可添加 
  4. 相册/地理位置 / icon

以下是实际效果。

tab + stack

想要实现的是tab嵌套stack 然后加上modal。这个感觉比较简单:

  1. 最外层是一个stack(modal)
  2. 最外层stack的第一个screen是一个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全部拷贝过来,直接可以用!简直完美。

稍微要处理的是react-navigation的修改,官网上有文档照着做就行。

注意一点,官网上的例子中对initState的处理可能需要改为如下:

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

具体代码就不放了,和官网例子差不多。 redux的好处是第一篇文章中所写的componentDidFocus hook可以删掉不用了。

expo的尝试

开发到这里后需要用camera等资源,就不可避免的需要npm run eject了,弹出的选项中默认第一个选项是使用expo框架,这是一个建立在react native之上的封装好的sdk,可以很方便的使用其他功能。我一开始也试用了以下,感觉还不错,camera/location等用起来非常简单。

不过最后在查看发布相关文档的时候发现,expo默认依赖于segment,这是个类似于google analytics的服务吧,因为这个的存在,打包送审的时候需要告诉apple用到了IDFA。

welogger不准备提供广告功能,而且segement免费tier是1000用户/月,我不想受这个限制。expo提供的动态更新js bundle功能还是很方便的,不过感觉自己搞一个服务器提供更新也不是什么难事,外加可以更深入去学习react native,就决定放弃expo。

去除expo也比较简单,新建一个react native工程,然后拷贝现有的js和依赖就行了。expo中用到的@expo/xx的component或功能,先注释掉,然后一个一个替换。

camera/photo

相机相关的处理用到了react-native-image-picker

安装上需要react-native link,没有global的rr,所以可以在npm script中增加一个local的命令。 新增的library基本都需要link一下。另外需要打开xocde中在Info.plist中增加对应权限请求,我打开xcode操作的,感觉比较简单。

使用方法很简单,参考其官方例子就行

关于图片上传,在web环境下一般使用FormData传送file,welogger服务器也是如此:

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

不过在react native中没有input.files, react-native做了一个特殊支持,只要递给一个含有uri的object就行了。注意貌似name才是field 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,不过在本身的rn中,polyfill了navigator.getCurrentPosition,所以welogger.com中用到的lib文件可以直接使用,赞。

welogge调用了google的API来获取位置的名字等信息。

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的icon font,在rn中也有类似的library。welogger用的是react-native-vector-icons,用起来非常简单。

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

注意loading indicator的话,rn提供了 ActivityIndicator

动态更新 header中的title

页面在初始化后调用API,根据获得的数据更新顶部的title,要实现这个需要用到react-navigation的setParams方法。

// 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);
    }
  }
}

总结

到此为止,整体app的界面跳转更加丰富了,redux的使用使得数据的管理变得非常简单。 接下来要做的主要就是UI 优化了,tab虽然比较标准易懂,但是不够酷,welogger作为一个未来的SNS,需要有一点更酷的界面组织方式。敬请期待。