GithubHelp home page GithubHelp logo

sunnylqm / react-native-storage Goto Github PK

View Code? Open in Web Editor NEW
3.0K 3.0K 271.0 1.06 MB

local storage wrapper for both react-native and browser. Support size controlling, auto expiring, remote data auto syncing and getting batch data in one query.

License: MIT License

JavaScript 100.00%
asyncstorage localstorage react-native reactjs sync

react-native-storage's Introduction

react-native-storage Backers on Open Collective Sponsors on Open Collective Build Status npm version

This is a local storage wrapper for both react native apps (using AsyncStorage) and web apps (using localStorage). ES6 syntax, promise for async load, fully tested with jest.

查看中文文档请点击 README-CHN.md

Install

npm install react-native-storage
npm install @react-native-async-storage/async-storage

or

yarn add react-native-storage
yarn add @react-native-async-storage/async-storage

Usage

Init

import Storage from 'react-native-storage';
import AsyncStorage from '@react-native-async-storage/async-storage';

const storage = new Storage({
  // maximum capacity, default 1000 key-ids
  size: 1000,

  // Use AsyncStorage for RN apps, or window.localStorage for web apps.
  // If storageBackend is not set, data will be lost after reload.
  storageBackend: AsyncStorage, // for web: window.localStorage

  // expire time, default: 1 day (1000 * 3600 * 24 milliseconds).
  // can be null, which means never expire.
  defaultExpires: 1000 * 3600 * 24,

  // cache data in the memory. default is true.
  enableCache: true,

  // if data was not found in storage or expired data was found,
  // the corresponding sync method will be invoked returning
  // the latest data.
  sync: {
    // we'll talk about the details later.
  }
});

export default storage;

Save & Load & Remove

// Save something with key only. (using only a keyname but no id)
// This key should be unique. This is for data frequently used.
// The key and value pair is permanently stored unless you remove it yourself.
storage.save({
  key: 'loginState', // Note: Do not use underscore("_") in key!
  data: {
    from: 'some other site',
    userid: 'some userid',
    token: 'some token'
  },

  // if expires not specified, the defaultExpires will be applied instead.
  // if set to null, then it will never expire.
  expires: 1000 * 3600
});

// load
storage
  .load({
    key: 'loginState',

    // autoSync (default: true) means if data is not found or has expired,
    // then invoke the corresponding sync method
    autoSync: true,

    // syncInBackground (default: true) means if data expired,
    // return the outdated data first while invoking the sync method.
    // If syncInBackground is set to false, and there is expired data,
    // it will wait for the new data and return only after the sync completed.
    // (This, of course, is slower)
    syncInBackground: true,

    // you can pass extra params to the sync method
    // see sync example below
    syncParams: {
      extraFetchOptions: {
        // blahblah
      },
      someFlag: true
    }
  })
  .then(ret => {
    // found data go to then()
    console.log(ret.userid);
  })
  .catch(err => {
    // any exception including data not found
    // goes to catch()
    console.warn(err.message);
    switch (err.name) {
      case 'NotFoundError':
        // TODO;
        break;
      case 'ExpiredError':
        // TODO
        break;
    }
  });

// --------------------------------------------------

// Save something with key and id.
// "key-id" data size cannot surpass the size parameter you pass in the constructor.
// By default the 1001st data will overwrite the 1st data item.
// If you then load the 1st data, a catch(NotFoundError) or sync will be invoked.
var userA = {
  name: 'A',
  age: 20,
  tags: ['geek', 'nerd', 'otaku']
};

storage.save({
  key: 'user', // Note: Do not use underscore("_") in key!
  id: '1001', // Note: Do not use underscore("_") in id!
  data: userA,
  expires: 1000 * 60
});

// load
storage
  .load({
    key: 'user',
    id: '1001'
  })
  .then(ret => {
    // found data goes to then()
    console.log(ret.userid);
  })
  .catch(err => {
    // any exception including data not found
    // goes to catch()
    console.warn(err.message);
    switch (err.name) {
      case 'NotFoundError':
        // TODO;
        break;
      case 'ExpiredError':
        // TODO
        break;
    }
  });

// --------------------------------------------------

// get all ids for "key-id" data under a key,
// note: does not include "key-only" information (which has no ids)
storage.getIdsForKey('user').then(ids => {
  console.log(ids);
});

// get all the "key-id" data under a key
// !! important: this does not include "key-only" data
storage.getAllDataForKey('user').then(users => {
  console.log(users);
});

// clear all "key-id" data under a key
// !! important: "key-only" data is not cleared by this function
storage.clearMapForKey('user');

// --------------------------------------------------

// remove a single record
storage.remove({
  key: 'lastPage'
});
storage.remove({
  key: 'user',
  id: '1001'
});

// clear map and remove all "key-id" data
// !! important: "key-only" data is not cleared, and is left intact
storage.clearMap();

Sync remote data(refresh)

There are two ways to set the sync method. You can pass the sync method in the constructor's parameter, as a function in an object, or you can define it at any time as shown below:

storage.sync = {
  // The name of the sync method must be the same as the data's key name
  // And the passed params will be an all-in-one object.
  // You can return a value or a promise here
  async user(params) {
    let {
      id,
      syncParams: { extraFetchOptions, someFlag }
    } = params;
    const response = await fetch('user/?id=' + id, {
      ...extraFetchOptions
    });
    const responseText = await response.text();
    console.log(`user${id} sync resp: `, responseText);
    const json = JSON.parse(responseText);
    if (json && json.user) {
      storage.save({
        key: 'user',
        id,
        data: json.user
      });
      if (someFlag) {
        // do something for some custom flag
      }
      // return required data when succeed
      return json.user;
    } else {
      // throw error when failed
      throw new Error(`error syncing user${id}`));
    }
  }
};

In the following example the sync method is called, when you invoke storage.load:

storage.load({
	key: 'user',
	id: '1002'
}).then(...)

If there is no user 1002 currently in storage, then storage.sync.user will be invoked to fetch and return the remote data.

Load batch data

// Load batch data with an array of `storage.load` parameters.
// It will invoke each key's sync method,
// and when all are complete will return all the data in an ordered array.
// The sync methods behave according to the syncInBackground setting: (default true)
// When set to true (the default), if timed out will return the current value
// while when set to false, will wait till the sync method completes

storage.getBatchData([
	{ key: 'loginState' },
	{ key: 'checkPoint', syncInBackground: false },
	{ key: 'balance' },
	{ key: 'user', id: '1009' }
])
.then(results => {
	results.forEach(result => {
		console.log(result);
	})
})

// Load batch data with one key and an array of ids.
storage.getBatchDataWithIds({
	key: 'user',
	ids: ['1001', '1002', '1003']
})
.then( ... )

There is an important difference between the way these two methods perform: getBatchData will invoke separate sync methods for each different key one after the other when the corresponding data is missing or not in sync. However, getBatchDataWithIds will collect a list of the missing data, pushing their ids to an array, and then pass the array to the single corresponding sync method once, reducing the number of requests, so you need to implement array query on the server side and handle the parameters of sync method properly. Note that the id parameter can be a single string or an array of strings.

You are welcome to ask any question in the issues page.

Contributors

This project exists thanks to all the people who contribute. [Contribute].

Backers

Thank you to all our backers! 🙏 [Become a backer]

Sponsors

Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor]

react-native-storage's People

Contributors

chengyu666 avatar chenzhitong avatar chriswong avatar dependabot[bot] avatar fer0x avatar henninghall avatar hi-rube avatar infinityblue avatar iwebdev1 avatar liganghui avatar ljcp avatar monkeywithacupcake avatar moonrailgun avatar nielsswinkels avatar pashute avatar sunnylqm avatar tong233 avatar wangshijun avatar weifuchuan avatar xwartz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-native-storage's Issues

How to use storage to find thru items?

For example I have list of items and want to find one with particular name / or pattern

What is the best way to achieve it? May be some docs would be good?

为什么 storage.load() 出来的结果都是 undefined

求助@sunnylqm
storage.save()没有抛异常,但是我调用storage.load()得到的结果都是undefined,我使用的版本是0.1.4。下面是我测试的一段代码:

storage.save({
            key: "name",
            rowData: {
                firstName: "tang",
                name: "henry"
            }
        }).then(() => {
            storage.load({
                key: "name"
            }).then(ret => {
                console.log("ret:" + ret);
            }).catch(error => {
                console.log("load error:" + error);
            })
        }).catch(error => {
            console.log("save error:" + error);
        });

读取储存的数据太慢了

React Native程序完全退出,进去后读取储存的数据太慢了,至少要半分钟,还没用过官方的,不知道是不是官方的读取也很慢

Saving data only works one time

when i remove application from task manager and start it again. saving data is not retrievable.

for example. when i saved
storage.save({ key: 'authentication', rawData: authentication });
and retrieve immediately using load it works and i get data but when i close application and come back calling load method does not return saving data. what can be the reason?

如何与redux 配合使用

react native + redux,如果我需要把数据存储在storage里,然后每次重新打开app的时候redux的store都读取storage中的数据,该如何实现,AsyncStorage是异步的导致没法在一开始初始化store的时候就读取到storage的数据,请问该怎么做比较好

JSON value '<null>' of type NSNull cannot be converted to NSString

load 数据为空时会报这个错

export const UNIONID = 'UNIONID';

let storage = new Storage({
size: Number.MAX_SAFE_INTEGER,
storageBackend: AsyncStorage,
defaultExpires: null,
enableCache: true,
sync: {
}
});

module.exports = {
Storage : storage
};

Global.Storage.load({
key: Global.UNIONID
}).then(ret => {
navigator.resetTo({ id: 'Home' });
}).catch(err => {
navigator.resetTo({ id: 'Activate' });
});

How exactly does defaultExpires/expires work?

In my store declaration, I have mentioned defaultExpires : 10 (10 milliseconds) just to test that if the data is not available in store, it should fetch the new data. But this logic doesn't seem to work. I have tried using expires: 10 in each save but that also doesn't work.

This is my store declaration :

export default storeData = new Storage({
size : 1000,
storageBackend : AsyncStorage,
defaultExpires : 10,
enableCache : true,
sync : {

}

});

This is my store saving data

storeData.save({
key: 'token',
rawData: {
token: jsonData.id_token
},
expires: 10
});

Here I am checking for token. In case it is not present, I fetch a new one

storeData.load({
key: 'token',
autoSync: true,
syncInBackground: true
}).then(user => {
console.log("token already found in store: " + user.token);
}).catch(err => {
console.log("error : " + err.name);
switch (err.name) {
case 'ExpiredError':
case 'NotFoundError':
console.log("token not found/expired. fetching new");
}
});

But everytime I load the app, I get "token already found in store".

How do I expire the token?

storage can not save Set Type

when i use it to save Set, I can't get Set type when load it
-------------------------save
let history = new Set();
history.add('test');

    // save storage
    gstorage.save({
        key: CONS.STORAGE_SEARCH_KEY,  // Note: Do not use underscore("_") in key!
        rawData: {
            history: history,
        },
        expires: null   // if set to null, then it will never expire.
    })

-----------------------------------load : (this.store_history).isSet()) return false
import _ from 'lodash';
gstorage.load({
key: CONS.STORAGE_SEARCH_KEY,
}).then(ret => {
// found data goes to then()
this.store_history = ret.history;
console.log('gstorage.load search:' +
(this.store_history).isSet());
}).catch(err => {
// any exception including data not found
// goes to catch()
console.log('gstorage.load search err: ' + JSON.stringify(err));
});

data missing

sometimes !!! on v1.0.6 the data is missing, when i killed the thread directly

expires的作用

expires: 30000
defaultExpires: 1000 * 30,
将时间设置成这个 但是数据一直在
过期时间的意思是什么?

size 的 问题

// 最大容量,默认值1000条数据循环存储
size: 1000,

问下, size 的大小是 数据库里最多1000 个表,还是 每个表里最多 1000条数据?

Question: How to save default app preferences when react-native app runs the first time?

storage.load cannot succeed unless storage.save had been executed.

What are some techniques to call storage.save to initialize my default app preferences when the app is run for the first time?

Is it recommended to call storage.load and if exception received call storage.save ?

I am a react-native and javascript newbie. Once I understand how to initialize storage parameters to default values, I plan to integrate with my existing redux reducer.

Thank you for sharing your hard work!

-Ed

'use strict';
import React, {
  AppRegistry,
  Component,
  StyleSheet,
  Text,
  View
} from 'react-native';

import Storage from 'react-native-storage';

var storage = new Storage({
  size: 1000,
  defaultExpires: 1000 * 3600 * 24,
  enableCache: true,
  sync : {
  }
});

class reactnativestoragetest extends Component {
  constructor(props) {
    super(props);
    this.state = {
      gpsEnabled: false,
      unitType: -1,
    };

    this.storageLoad.bind(this);
    this.storageLoaded.bind(this);
    this.storageInitDefaults.bind(this);
    this.render.bind(this);
  }
  // Is this the wrong place to do this?
  componentWillMount() {
    this.storageInitDefaults();
    this.storageLoad();
  }

  // Load my app settings 
  storageLoad(ret) {
    storage.load({
      key: 'appSettings',
      autoSync: true,
      syncInBackground: true
    }).then( ret => this.storageLoaded(ret)
  ).catch( err => {
    // Exception is thrown the 1st time app is run *if* storage.save had not been called
    console.warn('** Error: ', err);
  })
}

// found data - Why does changing state here not call render() ?
storageLoaded(ret) {
  console.log('*** storageLoaded ***');
  console.log('ret.gpsEnabled:', ret.gpsEnabled);
  console.log('ret.unitType:', ret.unitType);
  this.state = {
    gpsEnabled: ret.gpsEnabled,
    unitType: ret.unitType,
  };
}
// When app is run for first time, need to init my app settings
storageInitDefaults(ret) {
  console.log('*** storageInitDefaults ***');
  storage.save({
    key: 'appSettings',   // Note: Do not use underscore("_") in key!
    rawData: {
      gpsEnabled: true,
      unitType: 2,
    },
    expires: 1000 * 3600
  });
}
// How to make render be called after storageLoaded() receives the data? 
render() {
  console.log('*** render ***');

  return (
    <View style={styles.container}>
    <Text style={styles.welcome}>
    https://www.npmjs.com/package/react-native-storage
    </Text>
    <Text style={styles.instructions}>
    gpsEnabled: {this.state.gpsEnabled.toString()}
    </Text>
    <Text style={styles.instructions}>
    unitType: {this.state.unitType.toString()}
    </Text>
    </View>
  );
}
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});
AppRegistry.registerComponent('reactnativestoragetest', () => reactnativestoragetest);

将storage放到全局是不是需要react-native某个版本以上才行啊?

你好,我使用
global.storage = storage;
将storage放在全局,
之后在其它地方直接使用
storage.save({ key: 'loginInfo', rawData: obj, });
提示找不到storage这个变量,我想问一下是不是需要在使用的地方在import一下啊?如果这样的话,global这个还有什么用?

save之后取不到数据

想做保存登录状态的效果,先点击个人界面的登录跳转到登录界面输入用户名密码然后点击登录按钮获取数据,如果成功就把用户名密码保存,跳转到个人界面代码如下

if (responseJson.code == 1) {
                    this.setState({
                        isLogin: false,
                        msg: responseJson.msg
                    });
                    //存储数据
                    storage.save({
                        key: 'loginState',
                        rawData: {
                            from: 'Login',
                            userid: data,
                            token: 'some token'
                        },
                        expires: 1000 * 3600
                    });
                    this.props.navigator.pop();
                }

里面data是个对象包含用户名和密码
然后我在个人界面的render中读取数据

render() {
        //读取数据
        storage.load({
            key: 'loginState',
            autoSync: true,
            syncInBackground: true
        }).then(ret => {
            console.log(ret.userid);
            this.setState({
                username: ret.userid.username,
                pwd: ret.userid.password
            })
        }).catch(err => {
            console.warn(err.message);
            switch (err.name) {
                case 'NotFoundError':
                    // TODO;
                    break;
                case 'ExpiredError':
                    // TODO
                    break;
            }
        });
        return (
            <View style={styles.container}>
               <View style={styles.title}>
                <Text style={styles.titleText}>我的</Text>
               </View>
                {this.state.username!=''?
                    <View style={styles.loginBox}>
                    <Text>{this.state.username}</Text>
                   </View>:
                    <View style={styles.loginBox}>
                    <View style={styles.more}>
                      <Text>登录一下,了解更多</Text>
                    </View>
                    <TouchableHighlight underlayColor='#99d9f4' onPress={this.onSearchPressed.bind(this)} style={styles.loginButton}>
                    <Text style={styles.loginText}>登录/注册</Text>
                    </TouchableHighlight>
                   </View>
                }
                </View>)}

我的逻辑是读取数据并改变状态值,根据状态值来决定显示什么界面,但是依然还是个空字符串,感觉没有读取到数据,麻烦帮我看下,谢谢!

[bug]保存之后污染数据

调用了 save 方法之后,原数据会被污染,加上了一个 expires 的属性。
建议在 save 的时候,不改动原始数据,而是做一个深度拷贝。

What happens when maximum storage size fills up?

Also, I'm assuming the default of 1000 is 1MB, correct? Is there any way to query the current database size during development so we have an idea of how much space we actually need to allocate for production?

对于未save的key,直接通过load调用sync方法后,数据并不会存到storage中。

// storage.save({
      //   key: 'test',
      //   rawData: data,
      //   expires: null,
      // })
storage.load({
      key: 'test',
    }).then((data) => {
      // 将数据挂载到全局中
      this._provinces = data;
      console.log(storage);
    })

如果在load之前并没有save过任何关于key:test的数据,那么直接执行load方法后,打印出来的storage里面,依然不会存在key:test的任何数据。
这算是一个bug吗?

读取的方式是异步的

不能用async和await方式获取吗?

 async get(){
        var storage = new Storage();
        var user = await storage.load({key:'user'});
}

这样不行啊

How can I get all data along with corresponding ids

I have stored new data using

storage.save({
    key: 'user', 
    id: uuid.v4() // this generates a random string
    rawData: userA,
    expires: 1000 * 60   
});

But this id is not available when I fetch data using

// getAllDataForKey
storage.getAllDataForKey('user').then(users => {
    console.log(users);
});

size 的 问题

// 最大容量,默认值1000条数据循环存储
size: 1000,

问下, size 的大小是 数据库里最多1000 个表,还是 每个表里最多 1000条数据?

数据是异步读取的

看之前有位仁兄也提了这个问题,然而他说懂了就走了。。。
现在我的问题是,我的数据比如UUID是必须在主程序走之前加载完成,但是then链式调用,好像会先返回一个promise,主程序就开始走,之后才会执行then里面的,返回UUID,但是这样就错过了。用了async/await 也是这样。想问下,到底怎么样才能让我先取到数据再执行后面的程序,后面等待状态。菜鸟在线等

storage+redux, sync对象中的方法如何传递参数

storage.sync = {
myAttentionData(dispatch) {
fetch(REQUEST_URL)
.then(response => response.json())
.then(responseData => {
dispatch(endFetchData(responseData.itemlist));
storage.save({
key: 'myAttentionData',
rawData: responseData.itemlist
});
})
.done();
}
}

怎么才能把dispatch传过去呢?

react-native环境,导入后初始化,之后打印storage,没有找到相关方法

`var storage = new Storage({

size: 10000,

//数据过期时间,默认一年(1000 * 3600 * 24 * 365 秒)
defaultExpires: 1000 * 3600 * 24 * 365,

//读写时在内存中缓存数据。默认启用。
enableCache: true,

sync : {
//同步方法的具体说明会在后文提到
}
});

console.log(storage);`

_SIZE:10000
_innerVersion:10
_m:Object
_mapPromise:Promise
_s:Object
cache:Object
defaultExpires:31536000000
enableCache:true
isPromise:true
sync:Object

以上是log的输出,并没有save、load等方法可用,请问我是使用有问题吗?谢谢……

Max capacity (size) - what are the units?

Hi,

When setting the maximum capacity via size (e.g. size: 1000), what are the assumed units? Is this the number of key/value pairs? Or total bytes? Or something else?

Thank you in advance for your clarification.

Tom

Why is my sync method being called even when the object is not expired?

I read this comment in the sample code:

// if data was not found in storage or expired,
// the corresponding sync method will be invoked and return 
// the latest data.

I set defaultExpires to null so it should be saved with no expiration. But every time I try to load an object with storage.load, I can see that the sync method is being called. I know this because of console.log statements.

size 的问题

// 最大容量,默认值1000条数据循环存储
size: 1000,

问下, size 是 数据库里 最多1000个表, 还是 每个表里 最多 1000条数据 ?

怎么使用啊?

怎么使用啊?具体一点儿的例子有没有?
假如我想从storage中获取值,是用let value=storage.load({key:'key'})这种方式,还是
let value;
storage.load({key:'key').then(ret=>
value=ret;
);

saveBatchData?

Is it any chance to save a collection right away? Or should it be done item by item?

When I call storage.load with no ID, an empty object is passed to my sync method. Why?

If I run the code below, an object is passed to my sync method with resolve and reject. But if I omit the ID (in this case 1001), the object passed to the sync method is empty. Why?

storage.load({
    key: 'upoolActiveSports',
    id: 1001,
}).then(result => {
    console.log(result);
    this.setState({sports: result});
}).catch(err => {
    console.warn(err);
}).done(() => {
    this.setState({loading: false});
});

Unexpected use of reserved word 'export'

Hello, I tried to use this library but I'm getting an error on launch: SyntaxError: Unexpexted use of reserved word 'export'

image

I'm new to react-native. Am I missing an installation step?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.