Florin Pop
Florin Pop

Reputation: 5135

React Native initial database

I want to develop a React Native app, and I want to have locally stored some data of the format:

[
  {
    "id": 1,
    "text": "Lorem ipsum",
    "viewed": false
  },
  {
    "id": 2,
    "text": "Second lorem",
    "viewed": false
  },
  {
    "id": 3,
    "text": "Last lorem",
    "viewed": false
  }
]

I want this data to be available when the user first downloads the app. Then based on the user's navigation, the corresponding data in the db will change the viewed flag to true. This has to be persistent when the app will be updated by the user on the next version.

Any tips/ideas on how to achieve this?

Currently I'm looking into Realm.io but I'm not sure if this is the right tool for me.

Upvotes: 0

Views: 188

Answers (2)

Bhavan Patel
Bhavan Patel

Reputation: 1755

If data is more than pouchDb is suggested option.

You can install it with

npm install pouchdb-react-native --save

Simple Demo

import PouchDB from 'pouchdb-react-native'
const db = new PouchDB('mydb')

// use PouchDB
db.get('4711')
  .then(doc => console.log(doc))

Advance Demo

'use strict'

import React from 'react'
import {
  AsyncStorage,
  ListView,
  Navigator,
  StyleSheet,
  Text,
  TextInput,
  TouchableHighlight,
  View
} from 'react-native'

import ActionButton from 'react-native-action-button'
import PouchDB from 'pouchdb-react-native'

const localDB = new PouchDB('myDB')
console.log(localDB.adapter)

AsyncStorage.getAllKeys()
  .then(keys => AsyncStorage.multiGet(keys))
  .then(items => console.log('all pure Items', items))
  .catch(error => console.warn('error get all Items', error))

export default React.createClass({
  getInitialState () {
    const updateDocs = () => {
  localDB.allDocs({include_docs: true, limit: null})
    .then(result => {
      const items = result.rows.map(row => row.doc)
      const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1.id !== r2.id})
      this.setState({
        dataSource: ds.cloneWithRows(items),
        count: items.length
      })
    })
    .catch(error => console.warn('Could not load Documents', error, error.message))
}

localDB.changes({since: 'now', live: true})
  .on('change', () => updateDocs())

updateDocs()

return {
  dataSource: null,
  syncUrl: 'http://localhost:5984/test'
}
  },
  render () {
const renderScene = (route, navigator) => (
  <View style={{flex: 1, marginTop: 20, backgroundColor: '#FFFFFF'}}>
    {route.render()}
  </View>
)

const renderMain = () => {
  const insertAttachment = () => {
    const doc = {
      'title': 'with attachment',
      '_attachments': {
        'att.txt': {
          'content_type': 'text/plain',
          'data': 'TGVnZW5kYXJ5IGhlYXJ0cywgdGVhciB1cyBhbGwgYXBhcnQKTWFrZS' +
                  'BvdXIgZW1vdGlvbnMgYmxlZWQsIGNyeWluZyBvdXQgaW4gbmVlZA=='
        }
      }
    }

    localDB.post(doc)
      .then(result => console.log('save.attachment', result))
      .catch(error => console.warn('save.attachment.error', error, error.message, error.stack))
  }

  const insertRecords = count => {
    for (let index = 0; index < count; index++) {
      localDB.post({
        text: `Record ${index}/${count}`
      })
    }
  }

  const destroy = count => {
    localDB.destroy()
      .then(() => console.log('destroyed'))
      .catch(error => console.warn('destroyed', error))
  }

  const { dataSource } = this.state

  const renderSeparator = (sectionID, rowID) => (
    <View
      key={rowID}
      style={{borderColor: '#969A99', borderBottomWidth: StyleSheet.hairlineWidth}} />
  )

  const renderRow = (row) => {
    const updateItem = () => {
      const newRow = {...row}
      newRow.clickCount = newRow.clickCount ? newRow.clickCount + 1 : 1

      localDB.put(newRow)
        .then(result => console.log('Updated Item', result))
        .catch(error => console.warn('Error during update Item', error))
    }

    return (
      <TouchableHighlight onPress={updateItem}>
        <View key={row._id}>
          <Text style={{fontWeight: 'bold'}}>{row._id}</Text>
          <Text>{JSON.stringify(row, null, 4)}</Text>
        </View>
      </TouchableHighlight>
    )
  }

  const renderList = () => (
    <ListView
      dataSource={dataSource}
      renderRow={renderRow}
      renderSeparator={renderSeparator}
      enableEmptySections />
  )

  return (
    <View style={{flex: 1}}>
      <View>
        {!!this._sync && <Text style={{fontWeight: 'bold'}}>{this.state.syncUrl}</Text>}
        <Text style={{fontWeight: 'bold'}}>Count: {this.state.count}</Text>
      </View>
      <View
        style={{borderColor: '#969A99', borderBottomWidth: StyleSheet.hairlineWidth}} />
      {!dataSource
        ? (<Text>Loading...</Text>)
        : renderList()
      }
      <ActionButton buttonColor='#78B55E'>
        <ActionButton.Item
          buttonColor='#005BFF'
          title='Destroy Database'
          onPress={destroy}>
          <Text>destroy</Text>
        </ActionButton.Item>
        <ActionButton.Item
          buttonColor='#005BFF'
          title='Insert Attachments'
          onPress={insertAttachment}>
          <Text>attach</Text>
        </ActionButton.Item>
        <ActionButton.Item
          buttonColor='#005BFF'
          title='Insert 250 Records'
          onPress={() => insertRecords(250)}>
          <Text>insert</Text>
        </ActionButton.Item>
        <ActionButton.Item
          buttonColor='#005BFF'
          title='Sync'
          onPress={() => this._navigator.push({name: 'Sync', render: renderSync})}>
          <Text>sync</Text>
        </ActionButton.Item>
        <ActionButton.Item
          buttonColor='#005BFF'
          title='Add Item'
          onPress={() => this._navigator.push({name: 'AddItem', render: renderAddItem})}>
          <Text>+</Text>
        </ActionButton.Item>
      </ActionButton>
    </View>
  )
}

const renderButton = (text, onPress) => {
  return (
    <TouchableHighlight
      onPress={onPress}
      style={{
        flexDirection: 'column',
        paddingTop: 3,
        paddingBottom: 3,
        marginLeft: 10,
        marginRight: 10,
        backgroundColor: '#78B55E',
        borderRadius: 5
      }}>
      <Text
        style={{
          flex: 1,
          fontSize: 18,
          fontWeight: 'bold',
          color: '#FFFFFF',
          paddingLeft: 10,
          paddingRight: 10,
          paddingTop: 2,
          alignSelf: 'center'
        }}>
        {text}
      </Text>
    </TouchableHighlight>
  )
}

const renderSync = () => {
  const addSync = () => {
    if (this._sync) {
      this._sync.cancel()
      this._sync = null
    }

    if (this.state.syncUrl) {
      const remoteDb = new PouchDB(this.state.syncUrl, {ajax: {cache: false}})
      this._sync = PouchDB.sync(localDB, remoteDb, {live: true, retry: true})
        .on('error', error => console.error('Sync Error', error))
        .on('change', info => console.log('Sync change', info))
        .on('paused', info => console.log('Sync paused', info))
    }

    this._navigator.pop()
  }

  return (
    <View style={{flex: 1}}>
      <TextInput
        style={{
          height: 40,
          lineHeight: 40,
          fontSize: 16,
          paddingLeft: 10,
          paddingRight: 10
        }}
        autoFocus
        keyboardType='url'
        clearButtonMode='always'
        placeholder='enter URL'
        onChangeText={(text) => this.setState({syncUrl: text})}
        value={this.state.syncUrl} />
      {renderButton('Add Sync', addSync)}
    </View>
  )
}

const renderAddItem = () => {
  const addItem = () => {
    localDB.post(JSON.parse(this.state.newItem))
      .then(result => {
        this.setState({newItem: ''})
        this._navigator.pop()
      })
      .catch(error => console.error('Error during create Item', error, error.message))
  }

  return (
    <View style={{flex: 1}}>
      <TextInput
        style={{
          height: 340,
          lineHeight: 40,
          fontSize: 16,
          paddingLeft: 10,
          paddingRight: 10
        }}
        autoFocus
        clearButtonMode='always'
        multiline
        placeholder='JSON Object here'
        onChangeText={(text) => this.setState({newItem: text})}
        value={this.state.newItem} />
      {renderButton('Add Item', addItem)}
    </View>
  )
}

return (
  <View style={{flex: 1}}>
    <Navigator
      ref={navigator => { this._navigator = navigator }}
      renderScene={renderScene}
      initialRoute={{name: 'Main', render: renderMain}}
    />
  </View>
)
  }
})

Upvotes: 0

Ismailp
Ismailp

Reputation: 2383

You can use AsyncStorage for that like so:

const data = [
  {
    "id": 1,
    "text": "Lorem ipsum",
    "viewed": false
  },
  {
    "id": 2,
    "text": "Second lorem",
    "viewed": false
  },
  {
    "id": 3,
    "text": "Last lorem",
    "viewed": false
  }
]

try {
  await AsyncStorage.setItem(
    'SOME_KEY', JSON.stringify(data)
  );
} catch (error) {
  // Handle error 
}

Later on if you want to fetch/change you can do:

AsyncStorage.getItem('SOME_KEY', (err, result) => {
  console.log(JOSN.parse(result));
});

Upvotes: 1

Related Questions