【稀饭】react native 实战系列教程之数据存储

前端之家收集整理的这篇文章主要介绍了【稀饭】react native 实战系列教程之数据存储前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

概述

在开发一款APP,对于数据的存储是在正常不过了,在此之前,【稀饭】这个应用还没有用到存储数据的地方,为了学习研究React Native的数据存储,打算给应用增加【我的收藏】和【观看历史】这两个功能。接下来,我们来看看如何实现。

关于React Native数据存储的解决方

关于RN如何存储数据,有两种方案。
- AsyncStorage
- sqlite

第一种是官网提供的一种数据存储方案,它是一个简单的、异步的、持久化的Key-Value文件存储系统,它对于App来说是全局性的。如果你是个android的开发者,那么这就是类似于SharedPreferences,它适用于存储些系统设置、全局变量等简单的key-value数据,不适用于value过于庞大的数据,也不适用于一些包含数据结构等复杂数据;那么针对这种不足,我们需要借助sqlite,轻量的数据库,但RN并没有提供,如果你看完了之前的自定义模块 ,那么你也可以利用原生的sqliteDatabase开发自己的一个数据库。显然,这种需求很普遍,网上肯定有很多现有的轮子,我们就可以直接拿来用了。这里推荐使用 react-native-sqlite-storage

实现我的收藏功能

从上面的效果图来看,我们需要在详情页面增加一个收藏按钮,点击之后,空心图变实心图,然后在收藏列表里增加一条数据。要实现这个功能,需要在点击收藏之后,将需要的信息保存到数据库中,然后在列表页读取出来显示。这里利用react-native-sqlite-storage这个第三方库来实现数据的存储。

引入 react-native-sqlite-storage

安装

根据github上的文档说明(Android部分),首先我们在项目根目录下执行cmd命令:

npm install --save react-native-sqlite-storage

如果命令执行很久没有反应,建议换个npm镜像(淘宝镜像

配置

修改android项目的settings.gradle

// file: android/settings.gradle
...

include ':react-native-sqlite-storage'
project(':react-native-sqlite-storage').projectDir = new File(rootProject.projectDir,'../node_modules/react-native-sqlite-storage/src/android')

修改app\build.gradle

// file: android/app/build.gradle
...

dependencies {
    ...
    compile project(':react-native-sqlite-storage')
}

修改MainApplication.java,添加sqlitePluginPackage

public class MainApplication extends Application implements ReactApplication {

    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        @Override
        protected boolean getUseDeveloperSupport() {
            return BuildConfig.DEBUG;
        }

        @Override
        protected List<ReactPackage> getPackages() {
            return Arrays.<ReactPackage>asList(
                    new MainReactPackage(),new OrientationPackage(),new VideoViewPackage(),new sqlitePluginPackage()
            );
        }
    };

    @Override
    public ReactNativeHost getReactNativeHost() {
        return mReactNativeHost;
    }
}

到这里算是做好了前期的配置工作,下面结合实际需求讲述如何使用它。

接口封装

首先,我们需要封装一个sqlite模块,方便应用调用

在项目根目录js下新建db文件夹,然后在js/db/下新建sqlite.js

import React from 'react';
import sqliteStorage from 'react-native-sqlite-storage';

sqliteStorage.DEBUG(true);
const sqlite = React.createClass({
    render (){
        return null;
    },});
module.exports = sqlite;

该模块类似于工具类,不需要渲染任何界面,所以render return null。

定义打开数据open和关闭数据库close的方法

open(){
    db = sqliteStorage.openDatabase(
        database_name,database_version,database_displayname,database_size,()=>{
            this._successCB('open');
        },(err)=>{
            this._errorCB('open',err);
        });
},close(){
    if(db){
        this._successCB('close');
        db.close();
    }else {
        console.log("sqliteStorage not open");
    }
    db = null;
},

创建收藏表

字段 类型 说明
id INTEGER 主键
name VARCHAR 电影名称
actor VARCHAR 主演
time VARCHAR 收藏时间
pic VARCHAR 封面
url VARCHAR 详情地址
title VARCHAR 标题
createTable(){
    if (!db) {
        open();
    }
    //创建收藏表
    db.transaction((tx)=> {
        tx.executesql('CREATE TABLE IF NOT EXISTS ' + Collection_TABLE_NAME + '(' +
            'id INTEGER PRIMARY KEY NOT NULL,' +
            'name VARCHAR,' +
            'actor VARCHAR,' +
            'time VARCHAR,' +
            'pic VARCHAR,' +
            'url VARCHAR,' +
            'title VARCHAR'
            + ');',[],()=> {
                this._successCB('executesql');
            },(err)=> {
                this._errorCB('executesql',err);
            });
    },(err)=> {
        this._errorCB('transaction',err);
    },()=> {
        this._successCB('transaction');
    })
}

以上完整的代码

import React from 'react';
import sqliteStorage from 'react-native-sqlite-storage';

sqliteStorage.DEBUG(true);
var database_name = "xifan.db";
var database_version = "1.0";
var database_displayname = "MysqLite";
var database_size = -1;
var db;
const Collection_TABLE_NAME = "Collection";//收藏表

const sqlite = React.createClass({

    render(){
        return null;
    },componentWillUnmount(){
        if(db){
            this._successCB('close');
            db.close();
        }else {
            console.log("sqliteStorage not open");
        }
    },open(){
        db = sqliteStorage.openDatabase(
            database_name,()=>{
                this._successCB('open');
            },(err)=>{
                this._errorCB('open',createTable(){
        if (!db) {
            open();
        }
        //创建收藏表
        db.transaction((tx)=> {
            tx.executesql('CREATE TABLE IF NOT EXISTS ' + Collection_TABLE_NAME + '(' +
                'id INTEGER PRIMARY KEY NOT NULL,' +
                'name VARCHAR,' +
                'actor VARCHAR,' +
                'time VARCHAR,' +
                'pic VARCHAR,' +
                'url VARCHAR,' +
                'title VARCHAR'
                + ');',()=> {
                    this._successCB('executesql');
                },(err)=> {
                    this._errorCB('executesql',err);
                });
        },(err)=> {
            this._errorCB('transaction',err);
        },()=> {
            this._successCB('transaction');
        })
    },close(){
        if(db){
            this._successCB('close');
            db.close();
        }else {
            console.log("sqliteStorage not open");
        }
        db = null;
    },_successCB(name){
        console.log("sqliteStorage "+name+" success");
    },_errorCB(name,err){
        console.log("sqliteStorage "+name+" error:"+err);
    }
});

module.exports = sqlite;

然后在程序启动进入到首页时去创建表

MainScene.js

import sqlite from './db/sqlite';
var sqlite = new sqlite();
//省略其它代码
componentDidMount(){
    sqlite.createTable();
}

componentWillUnmount(){
    sqlite.close();
}

启动程序就可以看到成功执行了

以面向对象的思想来说,我们需要为收藏表的字段创建一个实体类对象Movie
在js/db下创建Movie.js

import React from 'react';
var id;
var name = "";
var actor = "";
var time = "";
var pic = "";
var url = "";
var title = "";

const Movie = React.createClass({
    render(){
        return null;
    },setId(id){
        this.id = id;
    },getId(){
        return this.id;
    },setName(name){
        this.name = name;
    },getName(){
        return this.name;
    },setActor(actor){
        this.actor = actor;
    },getActor(){
        return this.actor;
    },setTime(time){
        this.time = time;
    },getTime(){
        return this.time;
    },setPic(pic){
        this.pic = pic;
    },getPic(){
        return this.pic;
    },setUrl(url){
        this.url = url;
    },getUrl(){
        return this.url;
    },setTitle(title){
        this.title = title;
    },getTitle(){
        return this.title;
    }
});
module.exports = Movie;

接着,我们为收藏表增加增删查方法

saveCollection(movie){//保存收藏记录
    return new Promise((resolve,reject)=>{
        if(db){
            db.executesql(
                'INSERT INTO '+Collection_TABLE_NAME+' (name,actor,time,pic,url,title) VALUES(?,?,?)',[movie.getName(),movie.getActor(),movie.getTime(),movie.getPic(),movie.getUrl(),movie.getTitle()],()=>{
                    this._successCB('saveCollection');
                    resolve();
                },(err)=>{
                    this._errorCB('saveCollection',err);
                    reject();
                })
        }else {
            reject('db not open');
        }
    });

}
findCollectionByName(name){//通过影片名称获取对应收藏记录
    return new Promise((resolve,reject)=>{
        if(db){
            db.executesql('SELECT * FROM '+Collection_TABLE_NAME +' WHERE name=? LIMIT 1',[name],(results)=>{
                    console.log(results);
                    if(results.rows.length > 0){
                        resolve(results.rows.item(0));
                    }else {
                        reject('not find item');
                    }

                    this._successCB('findCollectionByName')
                },(err)=>{
                    reject(err);
                    this._errorCB('findCollectionByName',err)
                });
        }else {
            reject('db not open');
        }
    });

}
deleteCollectionByName(name){//通过影片名称删除对应收藏记录
    return new Promise((resolve,reject)=>{
        if(db){
            db.executesql('DELETE FROM '+Collection_TABLE_NAME +' WHERE name=?',()=>{
                    resolve();
                    this._successCB('deleteCollectionByName');
                },(err)=>{
                    reject(err);
                    this._errorCB('deleteCollectionByName',err);
                });
        }else {
            reject('db not open');
        }
    });

}
listCollection(pageSize,index){//获取收藏记录列表
    return new Promise((resolve,reject)=>{
        if(db){
            db.executesql('SELECT * FROM '+Collection_TABLE_NAME +' LIMIT '+pageSize+' OFFSET '+((index-1)*pageSize),(results)=>{
                    var len = results.rows.length;
                    var datas = [];
                    for(let i=0;i<len;i++){
                        datas.push(results.rows.item(i));
                    }
                    resolve(datas);
                    this._successCB('listCollection');
                },(err)=>{
                    reject(err);
                    this._errorCB('listCollection',err);
                });
        }else {
            reject('db not open');
        }
    });
}

这几个方法都使用到了Promise,这使得对象调用时可以使用链式的方法,更加方便。

接口调用

定义完接口,我们就可以按需求来实现了。在详情页DramaDetailScene.js添加一个收藏按钮,如效果图,这里不在阐述UI的实现,直接来看如何保存数据。

//收藏
_onCollectionPress(movie){
    console.log(movie);
    /*{ name: '信义',title: '全集中字',actor: '金喜善,李敏镐,刘德焕,朴世英,李必立,沈恩京,成勋,李民浩',pic: 'http://img.y3600.com/d/file/p/2016/10/26/40d39df617fc663a21f1e433e67742de.jpg',url: '/hanju/2016/958.html' }*/
    var isCollection = !this.state.isCollection;
    if(isCollection){//保存
        var coll = new Movie();
        coll.setName(movie.name);
        coll.setActor(movie.actor);
        coll.setPic(movie.pic);
        coll.setUrl(movie.url);
        coll.setTitle(movie.title);
        var date = new Date();
        var time=date.getFullYear()+'-'+(date.getMonth()+1)+'-'+date.getDate()+' ';
        var hours = date.getHours();
        if(hours < 9){
            time = time+'0'+hours+':';
        }else {
            time = time+hours+':';
        }
        var minutes = date.getMinutes();
        if(minutes < 9){
            time = time+'0'+minutes+':';
        }else {
            time = time+minutes+':';
        }
        var sec = date.getSeconds();
        if(sec < 9){
            time = time+'0'+sec;
        }else {
            time = time+sec;
        }
        coll.setTime(time);
        sqlite.saveCollection(coll).then(()=>{
            this.setState({
                isCollection:isCollection,});
        }).catch((e)=>{}).done();
    }else {//删除
        sqlite.deleteCollectionByName(this.props.data.name).then(()=>{
            this.setState({
                isCollection:isCollection,})
        }).catch((e)=>{}).done();
    }
}

我们通过this.state.isCollection来保存收藏状态。当未收藏时执行保存,已收藏时执行删除

再者,当我们一进入详情页时,需要知道是否已经收藏。

componentDidMount(){
    sqlite.findCollectionByName(this.props.data.name).then((result)=>{
        if(result){
            this.setState({
                isCollection:true,});
        }
    }).catch((e)=>{}).done();
    this._fetchData(this.props.data.url);
}

最后就是在我的收藏列表检索出所有的收藏记录并展示

_queryData(){
    sqlite.listCollection(10,index).then((results)=>{ datas = datas.concat(results); this.setState({ movies:this.state.movies.cloneWithRows(datas),isRefreshing:false }); }).catch((err)=>{ }).done(); }

【观看历史】也是差不多这个流程,具体的实现不在这里贴代码了,更多请查看我的github

猜你在找的React相关文章