React Native开发

ReactNative-利用redux来规范化代码

2018-12-03  本文已影响21人  Hozan

本篇博文涉及到的知识点有ES6的相关语法、redux的使用、realm数据库,如果你对此不太了解,下面推荐几个链接学习:

对于reactnative(简称rn)初学者而言,可能更注重于如何快速搭建项目应用和快速写业务功能,而对于代码的质量和规范化可能没那么讲究,这就可能会导致项目越来越大的时候,维护成本加大,而且可扩展性不强。基于此,本博文讲的是一些分层思想,如何使用redux让你的rn代码看起来更规范化一点。

前言

一般一个rn应用会涉及到UI的界面展示、网络请求服务器数据、数据库等。但很多时候,我们没有把他们分开写,可能一个js文件就有涉及UI界面渲染,网络请求的相关代码或者是其他的一些操作,这就导致代码臃肿难维护。倘若把各个层的代码分开来写,让他们既能交互,又互不干扰,这样就很好维护,代码可读性也能得到改善。
如下图1所示,各个层的分工大概是这样:

各个层要有自己的分工和定位,还要多封装一些模块出来,让代码更简化。

图1.png

下面通过代码来演示以上说的写法。

一、UI篇

本篇主要是讲UI如何通过reducer获取数据,以一个DataTestView.js为例子。

在DataTestView.js中只引入一个ContainerView ,这个ContainerView是什么呢?有什么作用?

import ContainerView from './container/MineDataContainerView'
export default class DataTestView extends Component {
    constructor() {
        super(...arguments)
    }
    render() {
        return (
            <View style={styles.root}>
                <ContainerView {...this.props}/>
            </View>
        )
    }
}

MineDataContainerView.js就是ContainerView,相当于一个容器,通过这个容器,我们能很好地利用redux来进行派发action操作和获取reducer数据给UI使用。
而MineDataUI才是真正展示给用户看的UI页面。

import {connect} from 'react-redux'
import MineDataUI from "../MineDataUI";
import {fetchPostsIfNeed} from "../action/MineDataActions";

const mapStateToProps = (state,props) => {
    return{
        data:state.mineData.data,
        isFetching:state.mineData.isFetching
    }
}
const mapDispatchToProps = dispatch => ({
    requestData: (reqParam) => dispatch(fetchPostsIfNeed(reqParam))
})
export default connect(mapStateToProps, mapDispatchToProps)(MineDataUI)

该UI页面只做两件事:
(1) this.props.requestData({name, psd}) 请求数据
(2) const {realityName, mobile, idNum, bankName} = this.props.data 获取数据进行UI渲染

export default class MineDataUI extends Component {
    constructor() {
        super(...arguments)
    }
    componentDidMount(){
        this._getData()
    }
    _getData = () => {
        const name = 'hozan'
        const psd = 5201314
        this.props.requestData({name, psd})
    }
    _gotoList = () => {
        InteractionManager.runAfterInteractions(() => {
            this.props.navigation.navigate('listtestview')
        })

    }
    render() {
        const {realityName, mobile, idNum, bankName} = this.props.data
        return (
            <View style={styles.container}>
                <View style={{flexDirection: 'row', marginTop: 20, marginBottom: 10}}>
                    <TouchableOpacity onPress={this._getData} style={styles.btn}>
                        <View>
                            <Text style={{color: 'white'}}>请求数据</Text>
                        </View>
                    </TouchableOpacity>
                    <TouchableOpacity onPress={this._gotoList} style={styles.btn}>
                        <View>
                            <Text style={{color: 'white'}}>跳去列表</Text>
                        </View>
                    </TouchableOpacity>
                </View>
                <ScrollView>
                    <View style={{marginHorizontal: 10,}}>
                        <Text>{'姓名:' + realityName}</Text>
                        <Text>{'手机:' + mobile}</Text>
                        <Text>{'身份证:' + idNum}</Text>
                        <Text>{'银行卡:' + bankName}</Text>
                    </View>
                </ScrollView>
                <LoadingView show={this.props.isFetching} text={'加载中...'}/>
            </View>
        )
    }
}

派发的相关action统一写在MineDataActions.js,这里涉及到以下一些主要的action:
1.开始发起请求的action
2.请求成功的action
3.请求失败的action
4.重置数据action
该js通过dispatch调起网络请求,然后根据请求成功或者失败派发了相关的action。

export const REQUEST_POST = 'REQUEST_POSTS'
export const REQUEST_SUCCESS = 'REQUEST_SUCCESS'
export const GET_DB_SUCCESS = 'GET_DB_SUCCESS'
export const REQUEST_FAIL = 'REQUEST_FAIL'
export const RESET_DATA = 'RESET_DATA'

//开始发起请求action
export const requestPosts = () => ({
    type: REQUEST_POST
})

//请求成功的action
export const reqSucc = (response) => ({
    type: REQUEST_SUCCESS,
    responseData: response
})

//请求失败的action
export const reqFail = () => ({
    type: REQUEST_FAIL
})

//读取数据库的数据成功
export const getDBDataSucc = (dbData) => ({
    type: GET_DB_SUCCESS,
    dbData
})

//重置数据action
export const resetData = () => ({
    type: RESET_DATA
})

//接口请求
const fetchPosts = (reqParams) => dispatch => {
    dispatch(requestPosts())
    return getMineData(reqParams)
        .then((model) => {
            if (model.result) {
                dispatch(getDBDataSucc(model.data))
            }
            return model.next()
        })
        .then((model) => {
            if (model.result) {
                dispatch(reqSucc(model.data))
            }
            EDMoney.Toast.show(model.data.msg)
        })
        .catch((error) => {
            dispatch(reqFail())
            EDMoney.Toast.show(error + '')
        })

}

//调用接口
const getMineData = async (reqParams) => {
    let RQ = new MineRQ(reqParams)
    const model = await RQ.requestData()
    return model
}

//是否需要请求 当缓存的值是可用时可减少网络请求
const shouldFetchPosts = (state) => {
    return true
}

export const fetchPostsIfNeed = reqParams => (dispatch, getState) => {
    if (shouldFetchPosts(getState())) {
        return dispatch(fetchPosts(reqParams))
    }
}
import {
    handleActions
} from 'redux-actions'
import {Record} from "immutable";
import {
    REQUEST_POST,
    REQUEST_SUCCESS,
    GET_DB_SUCCESS,
    REQUEST_FAIL,
    RESET_DATA
} from '../action/MineDataActions'

const userData = Record({
    isFetching: false,
    data: {},
}, 'userData')
const initState = new userData()

export default handleActions({
    [REQUEST_POST]: (state, action) => state.set('isFetching', true),
    [REQUEST_SUCCESS]: (state, action) => state.set('data', action.responseData)
        .set('isFetching', false),
    [GET_DB_SUCCESS]:(state,action)=>state.set('data',action.dbData),
    [REQUEST_FAIL]: (state, action) => state.set('isFetching', false),
    [RESET_DATA]: (state, action) => initState
}, initState)

二、请求数据篇

import FetchData from "../net/FetchData";
import MineDao from "../db/DAO/MineDao";
import UserListDao from "../db/DAO/UserListDao";

export default class BaseRQ {
    constructor(opt, reqParam) {
        this.opt = opt
        this.reqParam = reqParam
    }

    request = () => {
        let nextStep = async () => {
            let res
            res = await FetchData.fetchDataWithPost(this.opt, this.reqParam)
            if (res && res.error == 1) {
                //请求数据成功
                let totalInfo = Object.assign({}, {...res, requesttime})
                switch (this.opt) {
                    case '110':
                        let mineDao = new MineDao()
                        mineDao.insertTable({...totalInfo, name: this.reqParam.name})
                        break;
                    case '111':
                        let userListDao = new UserListDao()
                        userListDao.insertTable(totalInfo)
                        break;
                    default:
                }
                return {
                    data: totalInfo,
                    result: true,
                }
            } else {
                //请求数据不成功
                return {
                    data: res,
                    result: false
                }
            }

        }

        let dbData
        switch (this.opt) {
            case '110':
                let mineDao = new MineDao()
                dbData = mineDao.queryTableAll()
                if (dbData && dbData.length > 0) {
                    return {
                        data: JSON.parse(dbData[0].data),
                        next: nextStep,
                        result: true,
                    };
                } else {
                    return {
                        data: {},
                        next: nextStep,
                        result: false
                    }
                }
                break;
            case '111':
                let userListDao = new UserListDao()
                dbData = userListDao.queryTableAll()

                if (dbData && dbData.length > 0) {
                    let data = []
                    dbData.forEach((item) => {
                        data.push(JSON.parse(item.data))
                    })
                    return {
                        data: data,
                        next: nextStep,
                        result: true,
                    }
                } else {
                    return {
                        data: [],
                        next: nextStep,
                        result: false
                    }
                }
                break;
            default:
        }
    }
}
import BaseRQ from "./BaseRQ";
import {Test1Opt} from "../net/OptConfig";
import MineReqParam from "../model/requestparams/MineReqParam";

export default class MineRQ extends BaseRQ {
    constructor(reqParam: MineReqParam) {
        super(Test1Opt, {...reqParam})
    }

    requestData = () => {
        return this.request()
    }
}

三、数据库篇

import realm from "../index";

export default class BaseDao {
    constructor() {

    }
    //将服务器返回的数据保存到数据库对应的表
    _insertTable = (data, dbName) => {
        switch (dbName) {
            case 'Mine':
                let {requesttime, name} = data
                realm.write(() => {
                    let allData = realm.objects(dbName).filtered(`accName="${name}"`)
                    if (allData && allData.length > 0) {
                        allData[0].data = JSON.stringify(data)
                        allData[0].requesttime = requesttime
                    } else {
                        realm.create(dbName, {
                            accName: data.accName,
                            data: JSON.stringify(data),
                            requesttime: requesttime
                        })

                    }
                })
                break
            case 'UserList':
                let {userList} = data
                realm.write(() => {
                    let allData = realm.objects(dbName)
                    realm.delete(allData)
                    userList.forEach((item) => {
                        realm.create(dbName, {
                            id: item.id,
                            data: JSON.stringify(item),
                            requesttime: data.requesttime
                        })
                    })

                })
                break
            default:
        }
    }
    //删除数据库相关表的数据
    _deleteTable = (dbName) => {
        let Data = realm.objects(dbName)
        try {
            realm.write(() => {
                realm.delete(Data)
            })
        } catch (error) {
            console.log('error==' + error)
        }
    }
    //查询数据库相关表的数据
    _queryTableAll = (dbName) => {
        let allData = realm.objects(dbName)
        return allData
    }
}
import BaseDao from "./BaseDao";

export default class MineDao extends BaseDao {
    constructor() {
        super()
    }
    insertTable = (data) => {
        this._insertTable(data,'Mine')
    }
    deleteTable = () => {
        this._deleteTable('Mine')
    }
    queryTableAll = () => {
        return this._queryTableAll('Mine')
    }
}
import Realm from 'realm';
/**
 *测试表
 */

const Test = {
    name: 'Test',
    primaryKey: 'id',
    properties: {
        id: 'int',
        username:'string',
        password:'string'
    }
}

/**
 * 我的数据表
 */
const Mine={
   name:'Mine',
   properties:{
       accName:'string',
       data:'string',
   }
}
let realm=new Realm({
    schema:[Test,Mine],
    schemaVersion:1,
    migration:(oldRealm, newRealm)=>{

    }
})

export const clearCache = () => {
    realm.write(() => {
        realm.deleteAll()
    })
}

export default realm

关于realm数据库

移动端的数据存储推荐使用realm数据库,因为它被称为专为移动端而生的数据库,使用简单,高性能,支持reactnative,android和ios等,还有Realm Studio专门的调试工具。
这里不赘述它的使用,推荐一个realm使用教程,链接:Realm数据库在RN中的使用教程
下面这张图是来源于官网:

realm.png
官方释义

Realm Platform是通过快速和高效的同步协议连接的基于NoSQL的服务器和客户端组件的组合,以支持实时、连接的应用程序和服务,这些应用和服务具有响应性且不受网络状态的影响。
Realm Platform有两个主要组件:Realm Database(领域数据库)和 Realm Object Server(领域对象服务器)。

Realm Database嵌入在客户端上,是一个功能齐全、面向对象、跨平台的数据库,可以在设备上本地保存数据。它可用于主要的移动语言,如SWIFT和Object-C(IOS)、Java(Android)、C#(Xamarin,.NET)和JavaScript(Reactinative和Node.js)。Realm Database是轻量级和高性能,能够处理非常大的数据负载和很快地运行查询。基于"live objects"(实时对象),它与Realm Database实时无缝地同步数据,无需编写网络、序列化或对象关系映射代码。这意味着您的应用程序将能够尽快刷新数据。由于数据库的“live objects”特性,它也是编写反应性应用程序的完美伴侣。

Realm的统一数据模型扩展到Realm Object Server,它反映设备上的Realm Database。它作为移动应用程序体系结构中的中间件组件,管理数据同步、事件处理和与遗留系统的集成。Realm Object Server可以在多个设备之间同时高效地同步数据,并自动解决冲突-所有冲突都是实时的。此外,它提供了一个单一的地方来管理所有通信,包括遗留API事务,否则可能会受到移动网络延迟和其他问题的影响。Realm Object Server有一个灵活的部署模型,可以在任何Kubernetes支持的环境中自我托管,无论是在预置环境中还是在云环境中,比如AWS、Azure、IBMCloud或GCP。此外,Realm Object Server也可以用在 Realm Cloud(领域云),或者在多云环境中。这种灵活性避免了锁定,并确保了数据所有权和数据移动性。

看了官方的一点介绍,我也是有点云里雾里,后面找了两篇博文专门介绍了Realm Mobile Platform(简称RMP,realm移动端平台),对此做了以下几点总结:

以上几点总结源于下面两篇博文,关于realm一些更加深入的原理解释,下面两篇博文写的很详细,推荐浏览了解。

上一篇 下一篇

猜你喜欢

热点阅读