Estimote / react-native-proximity

React Native wrapper for Estimote Proximity SDK
Apache License 2.0
61 stars 38 forks source link

Implementation with redux #51

Open Jacky9425 opened 4 years ago

Jacky9425 commented 4 years ago

If you're using redux for your state management, a good idea might be to start the Proximity Observer where you create your redux store, and have the proximity events dispatch appropriate actions. The rest of your app can then subscribe to state/store changes as needed, and benefit from the proximity events this way. What a beautiful separation of concerns!

Hi, can you explain futher the statement above? Regarding Proximity Observer can be initialised during redux store creation, does the start() initialises at store.js or index.js where the redux store is integrated on the app?

nandorojo commented 4 years ago

I'm not sure exactly what they meant, but my guess would be that you initialize your proximity observer in the direct child of your redux Provider component. That way, any events that get fired can immediately dispatch actions from there, and you only have one listener set up.

My best guess

App.js

// ...
import * as RNEP from '@estimote/react-native-proximity'

export default () => {
  return (
    <Provider store={store}>
      <Observer />
    </Provider>
  )
}

const Observer = () => {
  const dispatch = useDispatch()
  useEffect(() => {
   // set up listener here
    const zone = new RNEP.ProximityZone(5, 'kitchen')
    zone.onEnterAction = (context) => {
     dispatch({ enteredKitchen: true })
    }
    zone.onExitAction = (context) => {
     dispatch({ enteredKitchen: false })
    }
  }, [dispatch])

  return <App />
}

What I would do

If you're like me, you like putting this kind of logic into custom react hooks, so that you can easily refactor code.

App.js

import * as React from 'react'
import { useObserver } from './use-observer.js'
import { Provider } from 'react-redux'
import { store } from '../path/to/your/store'

export default () => {
  return (
    <Provider store={store}>
      <Observer />
    </Provider>
  )
}

const Observer = () => {
  useObserver()
  return <App />
}

use-observer.js

import * as RNEP from '@estimote/react-native-proximity'
import { useDispatch } from 'react-redux'

export const useObserver = () => {
  const dispatch = useDispatch()
  useEffect(() => {
    // set up listener here
    const zone = new RNEP.ProximityZone(5, 'kitchen')
    zone.onEnterAction = context => {
      dispatch({ enteredKitchen: true })
    }
    zone.onExitAction = context => {
      dispatch({ enteredKitchen: false })
    }
  }, [dispatch])
}
jacky-ew commented 4 years ago

Thanks for the suggestion @nandorojo. However i am not planning to implement react hooks onto the project. However i've tried before dispatching action to modify states in redux store in background. It seems like i cannot access the state or props (normally we access the states thru mapStateToProps/mapDispatchToProps) to execute the functions that requires redux state. Therefore i might try to put the listener at the bottom level of the stack navigator (HomePage) to experiment first

nandorojo commented 4 years ago

You could also access the store object directly, without doing it through props, if you’re using background mode for beacons.

jacky-ew commented 4 years ago

any documentation as reference? @nandorojo

nandorojo commented 4 years ago

https://daveceddia.com/access-redux-store-outside-react/

store.js

import { createStore } from 'redux';
import reducer from './reducer';

export const store = createStore(rootReducer)

observer.js

import { store } from '../path/to/store'

...
zone.onEnterAction = context => {
  store.dispatch({ enteredKitchen: true })
}

You'll probably want to implement redux-persist if you want it to work while cached/in the background too. I'm not sure if this works, though, so maybe look into redux persist with background tasks.

jacky-ew commented 4 years ago

Hi, thanks for the reference. Im already using redux-persist, and the last experiment is that i cannot access the persisted state. store.getState() only returns the default state.

But for that case it was a headless task. I will try experiment more on that, thanks

nandorojo commented 4 years ago

This might help: https://github.com/rt2zz/redux-persist/issues/1066

It appears that you can dispatch actions, not sure about accessing state.

jacky-ew commented 4 years ago

Thanks for the suggestion.

i manage to access the states and actions when initialising the redux store with the app at App.js. So i suppose if i move the redux store initialisation to index.js, i should able to access the subscribed state and actions

index.js

const reduxApp = () => (
     <Provider store={store}>
         <App />
     </Provider>
)

console.log(store.getState()); <== should return me the tracked state instead of the default state/previous state

AppRegistry.registerComponent("sampleApp", () => reduxApp);