React Native - Menampilkan Data (Redux Storage) Part 1


Hello Sobat, Kali ini saya akan membahas mengenai cara membuat aplikasi React Native untuk menampilkan menggunakan ListView data dan menyimpan data menggunakan library Redux Storage, sebelum saya masuk ke langkah pembuatan aplikasi saya akan menjelaskan sedikit mengenai library redux storage, Redux Storage merupakan database lokal yang digunakan untuk melakukan penyimpanan dan library redux strorage juga bisa melakukan singkronisasi data, baik mungkin itu penjelasan singkat mengenai redux strorage jika ingin mengetahui lebih dalam mengenai libary ini kunjungi aja di sini, selanjutnya kita masuk ke langkah pembuatan aplikasinya.

Source Code

Create Aplikasi
Membuat aplikasi baru lihat vidio disini

Library
Menambahkan library masukan perintah pada terminal react-native <nama library> --save untuk lebih jelas lihat vidio disini, struktur library yang ditambahkan dalam bentuk json pada package.json




Class Aplikasi
Dibagian index.android.js tambahkan code berikut
import React, { Component } from 'react'
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux'
import * as storage from 'redux-storage'
import {
  Text,
  View,
  BackAndroid,
  AppRegistry
} from 'react-native'

import NavigationExperimental from 'react-native-deprecated-custom-components';

import AllSiswa from './app/components/view_allSiswa'

import ApplicationStore from './app/reducers'
const reducer = storage.reducer(ApplicationStore);

import createEngine from 'redux-storage-engine-reactnativeasyncstorage'
const engine = createEngine('notes-app-store')

const middleware = storage.createMiddleware(engine)
const createStoreWithMiddleware = applyMiddleware(middleware)(createStore)
const store = createStoreWithMiddleware(reducer)

const load = storage.createLoader(engine)
load(store)

const routes = [
  { component: AllSiswa }
]

class codeTRNote extends Component {
 componentDidMount() {
  }

  render() {
    return (
      <Provider store={store}>
        <NavigationExperimental.Navigator
          style={{ flex: 1 }}
          ref='nav'
          initialRouteStack={routes}
          renderScene={this.renderScene}
          configureScene={this.configureScene}
        />
      </Provider>
    )
  }
  renderScene(route, navigator) {
    return <route.component navigator={navigator} {...route.passProps}/>
  }
  configureScene(route, routeStack) {
    if (route.type == 'addingSiswa') {
      return NavigationExperimental.Navigator.SceneConfigs.FloatFromBottomAndroid
    }
    if (route.type == 'editingSiswa'){
      return NavigationExperimental.Navigator.SceneConfigs.FloatFromBottomAndroid
    }
    return NavigationExperimental.Navigator.SceneConfigs.FloatFromRight
  }
}

AppRegistry.registerComponent('codeTRNote', () => codeTRNote);
Folder
Struktur dari pembagian folder aplikasi, dari setiap folder harus dibuat secara manual mulai dari folder app di dalam folder app buat folder actions, components, lib, dan redurces




  • file index.js pada folder actions
export const ADD_SISWA = 'ADD_SISWA'
export const FETCH_SINGLE = 'FETCH_SINGLE'
export const UPDATE_SISWA = 'UPDATE_SISWA'
export const LOAD_STORED = 'LOAD_STORED'
export const DELETE_SISWA = 'DELETE_SISWA'

export function addSiswa(newSiswa) {
  return {
    type: ADD_SISWA,
    payload: newSiswa
  }
}

export function updateSiswa(updatedSiswa) {
  return {
    type: UPDATE_SISWA,
    payload: updatedSiswa
  }
}

export function deleteSiswa(siswaId) {
  return {
    type: DELETE_SISWA,
    payload: siswaId
  }
}
  • file style.js pada folder components
export const styles = {
  allSiswaContainer: {
    flex: 1,
    backgroundColor: '#ffffff'
  },
  emptyListContainer: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    marginBottom: 56
  },
  emptyList: {
    fontFamily: 'Lato-Bold',
    fontSize: 16
  },

  addNotesContainer: {
    flex: 1,
    backgroundColor: '#ffffff'
  },
  textInputContainer: {
    flex: 1
  },
  inputSiswaStyle: {
    height: 60,
    paddingTop: 5,
    paddingLeft: 20,
    paddingRight: 20,
    paddingBottom: 0,
    fontFamily: 'Lato-Regular',
    fontSize: 20
  },
}
  • file view_allSiswa.js pada folder components
import React, { Component } from 'react'
import {
  Text,
  View,
  StatusBar,
  TextInput,
  Alert,
  BackAndroid,
  ListView
} from 'react-native'
import { connect } from 'react-redux'

import NewSiswa from './view_newSiswa'
import SingleSiswa from './view_singleSiswa'
import Toolbar from '../lib/Toolbar'
import SiswaViewCard from '../lib/SiswaViewCard'
import AddSiswaButton from '../lib/AddSiswaButton'
import { deleteSiswa } from '../actions'
import { styles } from './styles'
import { getColor } from '../lib/helpers'
import { Typo } from '../lib/Typography'
import Icon from '../lib/Icon'

class AllSiswa extends Component {
  constructor(props) {
    super(props)

    this._handleBackButton = this._handleBackButton.bind(this)
  }

  componentDidMount() {
    BackAndroid.addEventListener('hardwareBackPress', this._handleBackButton)
  }

  componentWillUnmount() {
    BackAndroid.removeEventListener('hardwareBackPress', this._handleBackButton)
  }

  _handleBackButton() {
    if (this.props.navigator.getCurrentRoutes().length == 1) {
      return false
    }
    return true
  }

  render() {
    return (
      <View style={ styles.allSiswaContainer }>
        <StatusBar
          backgroundColor={getColor('paperBlue700')}
          barStyle="light-content"
          animated={true}
        />
        <Toolbar title="Data Siswa" color={getColor('paperBlue')}/>
        { this.renderList() }

        <AddSiswaButton onBtnPress={this.addNewSiswa.bind(this)}/>
      </View>
    )
  }

  addNewSiswa() {
    this.props.navigator.push({component: NewSiswa, type: 'addingSiswa'})
  }

  goToSiswa(siswaId, nis, nama) {
    this.props.navigator.push({ component: SingleSiswa, type: 'editingSiswa', passProps: { siswaId, nis, nama } })
  }

  longPressSiswa(siswaId) {
    Alert.alert(
      'Hapus Siswa',
      'Yakin ingin Hapus?',
      [
        { text: 'Ya', onPress: () => this.deleteSiswa(siswaId) },
        { text: 'Tidak' }
      ]
    )
  }

  deleteSiswa(siswaId) {
    this.props.deleteSiswa(siswaId)
  }

  renderList() {
    if (this.props.siswa.length <= 0) {
      return (
        <View style={styles.emptyListContainer}>
          <Icon name="delete" size={180} color={getColor('#f0f0f0')} />
          <Text style={styles.emptyList}>Data Siswa Masih Kosong</Text>
        </View>
      )
    } else {
      var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
      var dataSource = ds.cloneWithRows(this.props.siswa) || []

      return (
        <ListView
          dataSource={dataSource}
          renderRow={(siswa, sectionID, rowID) => {
            return (
              <SiswaViewCard
                nis={siswa.nis}
                nama={siswa.nama}
                id={siswa.id}
                keys={rowID}
                onPressBtn={this.goToSiswa.bind(this)}
                onLongPressBtn={this.longPressSiswa.bind(this)}
              />
            )
          }}
        />
      )
    }
  }
}

function mapStateToProps(state) {
  return { siswa: state.allSiswa }
}

export default connect(mapStateToProps, { deleteSiswa })(AllSiswa)
  • file AddSiswaButton.js pada folder lib
import React, { Component } from 'react'
import {
  View,
  Text,
  StyleSheet,
  TouchableOpacity
} from 'react-native'

import { Typo } from './Typography'
import { getColor } from './helpers'
import Icon from './Icon'

export default class AddSiswaButton extends Component {
  render() {
    return (

        <View style={styles.container}>
          <TouchableOpacity onPress={this.handlePress.bind(this)}>
            <Icon name="add-circle" size={56} color={getColor('paperBlue')} />
          </TouchableOpacity>
        </View>
    )
  }

  handlePress() {
    this.props.onBtnPress()
  }
}

const styles = StyleSheet.create({
  container: {
    position: 'absolute',
    bottom: 15,
    right: 15,
    backgroundColor: 'white',
    borderRadius: 50
  }
})
  • file BackBtn.js pada folder lib
import React, { Component } from 'react'
import {
  View,
  Text,
  StyleSheet,
  TouchableOpacity
} from 'react-native'

import { Typo } from './Typography'
import { getColor } from './helpers'
import Icon from './Icon'

export default class BackBtn extends Component {
  render() {
    return (

        <View style={styles.container}>
          <TouchableOpacity onPress={this.handlePress.bind(this)}>
            <Icon name="close" size={36} color={getColor('#ffffff')} />
          </TouchableOpacity>
        </View>
    )
  }

  handlePress() {
    this.props.onBtnPress()
  }
}

const styles = StyleSheet.create({
  container: {
    position: 'absolute',
    padding: 5,
    bottom: 15,
    left: 15,
    backgroundColor: getColor('paperPink'),
    borderRadius: 50
  }
})
  • file config.js pada folder lib
import { typography, color } from './paper_styles'

export const COLOR = color
export const TYPO = typography
export const PRIMARY = 'paperBlue'
export const COLOR_NAME = [
  'googleBlue',
  'googleGreen',
  'googleGrey',
  'googleRed',
  'googleYellow',
  'paperAmber',
  'paperBlue',
  'paperBlueGrey',
  'paperBrown',
  'paperCyan',
  'paperDeepOrange',
  'paperDeepPurple',
  'paperGreen',
  'paperGrey',
  'paperIndigo',
  'paperLightBlue',
  'paperLightGreen',
  'paperLime',
  'paperOrange',
  'paperPink',
  'paperPurple',
  'paperRed',
  'paperTeal',
  'paperYellow'
]
export const THEME_NAME = ['light', 'dark'];
  • file helpers.js pada folder lib
import { PRIMARY, COLOR } from './config'

// Detect whether a color is a hex code/rgba or a paper element style

export function getColor(string) {
  if (string) {
    if (string.indexOf('#') > -1 || string.indexOf('rgba') > -1
        || string.indexOf('rgb') > -1) {
      return string
    }

    if (COLOR[string]) {
      return COLOR[string].color
    }

    if (COLOR[`${string}500`]) {
      return COLOR[`${string}500`].color
    }
  }

  return COLOR[`${PRIMARY}500`].color
}
  • file icon.js pada folder lib
import React, { Component, PropTypes } from 'react'
import { View } from 'react-native'
import { default as VectorIcon } from 'react-native-vector-icons/MaterialIcons'

import { getColor } from './helpers'

// example ->
// <Icon name="google" size={24} color={COLOR[`${primary}500`].color} />

export default class Icon extends Component {

  static propTypes = {
    name: PropTypes.string.isRequired,
    style: View.propTypes.style,
    size: PropTypes.number,
    color: PropTypes.string,
    allowFontScaling: PropTypes.bool
  }

  static defaultProps = {
    size: 30,
    color: '#757575',
    allowFontScaling: true
  }

  render() {
    const {
      name,
      style,
      size,
      color,
      allowFontScaling
    } = this.props

    return (
      <VectorIcon
        name={name}
        size={size}
        color={getColor(color)}
        style={style}
        allowFontScaling={allowFontScaling}
      />
    )
  }
}
  • file paper_style.js pada folder lib download disini
  • file SiswaViewCard.js pada folder lib
import React, { Component } from 'react'
import {
  View,
  Text,
  TouchableOpacity,
  StyleSheet
} from 'react-native'

import { Typo } from './Typography'
import { getColor } from './helpers'

export default class SiswaViewCard extends Component {
  render() {
    const {
      nis,
      nama,
      id,
      keys
    } = this.props

    const background = (keys % 2 == 0) ? { backgroundColor: '#ffffff' } : { backgroundColor: '#f2f2f2'}

    return (
      <TouchableOpacity onPress={this.handleGoto.bind(this)} onLongPress={this.handleLongPress.bind(this)}>
        <View style={[ styles.cardContainer, background ]}>
          <View style={styles.cardTitleContainer}>
            <Text style={[ styles.cardTitle, Typo.cardTitle ]}>
              {nis.toUpperCase()}
            </Text>
          </View>
          <View style={styles.cardDescriptionContainer}>
            <Text style={[ styles.cardDescription, Typo.cardDescription ]}>
              {(nama.length > 30)
              ? nama.slice(0, 30) + '...'
              : nama}
            </Text>
          </View>
        </View>
      </TouchableOpacity>
    )
  }

  handleLongPress() {
    this.props.onLongPressBtn(this.props.id)
  }

  handleGoto() {
    this.props.onPressBtn(this.props.id, this.props.nis, this.props.nama)
  }
}

const styles = StyleSheet.create({
  cardContainer: {
    paddingLeft: 10,
    paddingRight: 10,
    paddingTop: 15,
    paddingBottom: 15
  },
  cardTitleContainer: {
    justifyContent: 'center'
  },
  cardTitle: {
    marginBottom: 10
  },
  cardDescriptionContainer: {

  },
  cardDescription: {

  }
})
  • file TickBtn.js pada folder lib
import React, { Component } from 'react'
import {
  View,
  Text,
  StyleSheet,
  TouchableOpacity
} from 'react-native'

import { Typo } from './Typography'
import { getColor } from './helpers'
import Icon from './Icon'

export default class TickBtn extends Component {
  render() {
    return (

        <View style={styles.container}>
          <TouchableOpacity onPress={this.handlePress.bind(this)}>
            <Icon name="save" size={36} color={getColor('#ffffff')} />
          </TouchableOpacity>
        </View>
    )
  }

  handlePress() {
    this.props.onBtnPress()
  }
}

const styles = StyleSheet.create({
  container: {
    position: 'absolute',
    padding: 5,
    bottom: 15,
    right: 15,
    backgroundColor: getColor('paperBlue'),
    borderRadius: 50
  }
})
  • file Toolbar.js pada folder lib
import React, { Component } from 'react'
import {
  View,
  Text,
  StyleSheet
} from 'react-native'

import { Typo } from './Typography'
import { getColor } from './helpers'

export default class Toolbar extends Component {
  render() {
    const {
      color,
      title
    } = this.props

    return (
      <View style={[ styles.toolbar, { backgroundColor: getColor(color) } ]}>
        <Text style={[ styles.title, Typo.toolbarTitle ]}>
          {title.toUpperCase()}
        </Text>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  toolbar: {
    height: 56,
    justifyContent: 'center'
  },
  title: {
    marginLeft: 16,
    color: 'white'
  }
})
  • file Typography.js pada folder lib
export const Typo = {
  toolbarTitle: {
    fontFamily: 'Lato-Bold',
    fontSize: 18
  },
  cardTitle: {
    fontFamily: 'Lato-Bold',
    fontSize: 16
  },
  cardDescription: {
    fontFamily: 'Lato-Regular',
    fontSize: 14,
    lineHeight: 20
  }
}
  • file index.js pada folder reducers
import { combineReducers } from 'redux'
import SiswaReducer from './reducer_siswa'
import CurrentSiswaReducer from './reducer_current'

const rootReducer = combineReducers({
  allSiswa: SiswaReducer,
  currentSiswa: CurrentSiswaReducer
})

export default rootReducer
  • file reduces_current.js pada folder reducers
import { FETCH_SINGLE } from '../actions'

export default (state = {}, action) => {
  switch(action) {
    case FETCH_SINGLE:
      return action.payload

    default:
      return state
  }
}
  • file reduces_siswa.js pada folder reducers
import _ from 'lodash'
import { ADD_SISWA, UPDATE_SISWA, DELETE_SISWA } from '../actions'

const initialState = [
]

export default (state = initialState, action) => {
  switch(action.type) {
    case ADD_SISWA:
      const newSiswa = Object.assign({}, { id: state.length }, action.payload)
      const newState = [newSiswa, ...state]
      return newState

    case UPDATE_SISWA:
      const newArray = _.remove(state, (data) => {
        return data.id != action.payload.id
      })
      const updatedSiswa = Object.assign({}, { id: newArray.length }, action.payload)
      const updatedState = [updatedSiswa, ...newArray]
      return updatedState

    case DELETE_SISWA:
      console.log('get request')
      console.log(action.payload)
      const deletedNewArray = _.remove(state, (data) => {
        return data.id != action.payload
      })
      return deletedNewArray

    default:
      return state
  }
}
Untuk penempatan aplikasi lebih jelasnya lihat vidio 




Output Aplikasi



Sekian artikel dari saya semoga bermanfaat, jika ada yang ditanyakan comment saja di bawah.

Sumber github (editing source code)

Comments

Post a Comment

Popular posts from this blog

Menampilkan Data Menggunakan RecyclerView (Volley) Part 1

Menampilkan Data Menggunakan RecyclerView (Retrofit) Part 1

Menampilkan Data Menggunakan RecyclerView (Firebase) Part 1