ReactNative开发的一些经验

·  阅读 1475

ReactNative开发的一些经验

本文主要是本人从Android原生到开发出一套完整的RN模块嵌入原生的经验。

state与props

他们二者是RN的数据核心。state主要是自身数据更新,props主要是父组件传入到子组件中使用,也会有自身的默认props。 对于props,建议一般都传入子组件,防止以后进行修改。特定的flag则进行指定,比如:有一个Page A,他包含Component B

A extends Component{
...
render(){
    return (
        ...
        <B url=this.state.url {...this.state}/>
        ...
    );
}
...
}
复制代码

对于State,需要注意死循环,这种情况可能发生在render过程(不是事件响应过程),假如在渲染过程中,我们需要修改state,一般先进行判断,然后再去设置,否则可能一直死循环render比如下面的ImageHolderComponent例子。

Component

在RN中,提倡组件化。他是可以根据UI划分组件,对于底层组件,我们需要实现逻辑不需要实现业务。比如,对于styles,我们一般需要提供一个默认样式,然后再通过外部插入的props样式,通过数组组合一起,外部可以覆盖内部。 例如,下面例子是定义一个通用的Image组件,他可以在图片加载失败或者是URL为空等适合显示出place holder图片。


export default class ImageHolderComponent extends Component {
    static defaultProps = {
        url: "",
        imageStyle: {},
        holderResizeMode: "cover"
    };

    constructor(props) {
        super(props);
        this.state = {
            loadState: 0
        }
    }

    _emptyUri = () => {
        return <Image
            resizeMode={this.props.holderResizeMode}
            source={this._holder()}

            style={[{
                width: 60,
                height: 60,
                borderRadius: 60,
            }, this.props.imageStyle]}/>;
    };

    _normal = () => {
        return <Image
            source={{uri: this.props.url}}
            defaultSource={this._holder()}
            onError={() => {
                if (this.state.loadState === 1) {
                    //防止死循环
                    return;
                }
                this.setState({loadState: 1});
            }}
            onLoad={() => {
                if (this.state.loadState === 2) {
                    ////防止死循环
                    return
                }
                this.setState({loadState: 2});
            }
            }
            style={[{
                width: 60,
                height: 60,
                borderRadius: 60,
            }, this.props.imageStyle]}/>;
    };

    _holder = () => {
        const {holder} = this.props;
        if (holder) {
            return holder();
        }
        return require("../../img/pic_empty_data.png");
    };

    render() {
        const showView = this.props.url && (this.props.url.indexOf("http") >= 0) && this.state.loadState !== 1 ? this._normal() : this._emptyUri();
        return (
            <TouchableWithoutFeedback onPress={() => {
                const {onPress} = this.props;
                if (onPress) {
                    onPress(this.props.url);
                }
            }}>
                {showView}
            </TouchableWithoutFeedback>
        );
    }
}
复制代码

他需要外部传入一个url即可,假如需要复写样式则传入参数名为imageStyle的style即可。 这样子,就做到Component独立,又能与外部进行交互。

fetch封装

因为fetch是返回一个Promise的,所以还是比较友好的。 我们可以对fetch进行一个比较好的封装,需求是:

  1. 外部需要传入参数即可
  2. 返回一个Promise
  3. 对于基本错误,我们能够catch返回。

function _body(method, params) {
    const _params = JSON.stringify(params);
    console.log(("http=> params = " + _params));
    return {method: method, headers: HEADER, body: _params}
}


function _get(url, params) {
    return _request(url, _body("GET", params));
}


function _post(url, params) {
    return _request(url, _body("POST", params))
}

//发起请求
function _request(url, params, timeout) {
    console.log(("http=> url = " + url));
    const request = fetch(url, params)
        .then(result => {
            if (result.ok) {
                console.log(("http=> respond = " + result.json()));
                return result.json();
            } else {
                return Promise.reject({code: -1, message: "请求失败"});
            }

        })
        .then(value => {
            //可以进行进一步刷选
            return value;
        })
        .catch(error => {
            return Promise.reject(error);
        });
    return _wrapRequest(request, timeout)
}

//设置超时,默认10000
function _wrapRequest(promise, timeout = 10000) {
    return Promise.race([promise, new Promise(function (resolve, reject) {
        setTimeout(() => {
                reject({code: -1, message: "请求超时"});
            },
            timeout);
    })]).catch(e => {
        return Promise.reject(e)
    })
}

复制代码

比如对于登陆而言

//登陆
function _body(password,username) {
    const params = {};
    params[USER_NAME] = username;
    params[PASSWORD] = password;
    ....
    return _post("loginurl",params)
}

复制代码

就可以了。

启用代码检测

个人认为这个还是比较有必要的,这样子可以统一代码风格,什么分号,括号,无用导入,一些可能出现错误的地方,通过ESLint都可以检测出来。需要可以前往ReactNativeDemo 查看.eslint文件和package.json文件。

分包+代码

在开发中,一般独立页面使用Page结尾,一般组件使用Component结尾。 对于对内方法添加下划线,对外方法就不用,还是使用驼峰的命名规则。 一般,我们尽量使用解构赋值取数据,这样子可以给一些默认值。

方法问题

在RN中,他相对于CSS顶层的this是window,RN顶层的this是本身Component,所以我们尽量使用方法变量而不是定义方法,这样子他就可以自动绑定this了。例如

A extends Component{
    //不推荐
    methodA(){
        //...
    }
    
    //推荐
    methodB = ()=>{
        //。。
    }
}
复制代码

navigate

我们进行页面跳转的时候,比如使用的是react-navigation,我们尽量带一个from这样子一个key,value是该页面的String值。这样子方便下一个页面无论是goback还是navigate去别的页面在过把存款都比较好操作

Android物理back键

对于Android物理back键,我们可能在特殊的页面需要进行特殊处理,比如back的时候需要弹窗什么的。 但是,在RN中,比如一个需求是A.navigate(B),然后B.navigate(C)。假如是有这样子一个跳转关系,在B中需要监听back键,这时候在C中没有监听,在C中按下back键就会把B也结束掉,因为B中的监听并没有被移除,而在C中,他是没有处理back event的返回值的,导致B响应了,就连续返回了。 需要处理该问题,我们就需要定义一个BaseComponent,以后每一个独立页面Page,都需要继承该Component,假如是有特殊处理的,需要注意super。

function log(msg) {
    console.log("BaseComponentPage==>" + msg);
}

export default class BaseComponentPage extends Component {
    constructor(props) {
        super(props);
        log("addEventListener hardwareBackPress");
        BackHandler.addEventListener("hardwareBackPress", this._onBackPress);
    }
    //子类需要重写该方法到时候,必须super
    componentWillUnmount() {
        log("removeEventListener hardwareBackPress");
        BackHandler.removeEventListener("hardwareBackPress", this._onBackPress);
    }

    _onBackPress = () => {
        this.props.navigation.goBack();
        log("_onBackPress");
        return true;
    };
}

复制代码

常用第三方开源Package

  • react-navigation 导航
  • react-native-router-flux 导航,他更加友好的封装了react-navigation
  • react-reduxreduxredux-loggerredux-thunk redux全家桶
  • react-native-scrollable-tab-view 可以做到类似Android的TabLayout
  • prop-types 类型检测
  • react-native-video 视频播放
  • react-native-image-zoom-viewer图片缩放
  • react-native-fetch-blob 网络请求,也可以自己直接封装一下fetch。
  • react-native-cached-image 图片缓存+显示