2017-07-06 10:08:25 +00:00
# Usage with Redux
Although RNFirebase usage requires a React Native environment, it isn't tightly coupled which allows for full flexibility
2017-08-26 05:48:26 +00:00
when it comes to integrating with other modules such a [`react-redux` ](https://github.com/reactjs/react-redux ).
2017-08-26 23:00:08 +00:00
## React Redux Firebase
2017-07-06 10:08:25 +00:00
2017-08-28 22:54:56 +00:00
[`react-redux-firebase` ](http://docs.react-redux-firebase.com/history/v2.0.0 ) provides simplified and standardized common redux/firebase logic.
To add `react-redux-firebase` to your project:
2017-08-31 07:29:23 +00:00
1. Make sure you already have `redux` , `react-redux` , `redux-thunk` installed (if not, run `npm i --save redux react-redux redux-thunk` )
2017-08-28 22:54:56 +00:00
1. Run `npm i --save react-redux-firebase@canary` *we point to canary here to get current progress with v2.0.0*
2017-08-31 07:24:43 +00:00
1. Add `firebaseStateReducer` under `firebase` key within reducer:
2017-08-28 22:54:56 +00:00
2017-08-31 00:59:38 +00:00
**reducers.js**
```js
2017-08-31 07:24:43 +00:00
import { combineReducers } from 'redux';
import { firebaseStateReducer } from 'react-redux-firebase';
2017-08-31 00:59:38 +00:00
export const makeRootReducer = (asyncReducers) => {
return combineReducers({
// Add sync reducers here
firebase: firebaseStateReducer,
...asyncReducers
2017-08-31 07:24:43 +00:00
});
};
2017-08-28 22:54:56 +00:00
2017-08-31 07:24:43 +00:00
export default makeRootReducer;
2017-08-31 00:59:38 +00:00
2017-08-31 08:17:44 +00:00
// *Optional* Useful for injecting reducers as part of async routes
2017-08-31 00:59:38 +00:00
export const injectReducer = (store, { key, reducer }) => {
store.asyncReducers[key] = reducer
store.replaceReducer(makeRootReducer(store.asyncReducers))
2017-08-31 07:24:43 +00:00
};
2017-08-31 00:59:38 +00:00
```
2017-08-31 07:29:23 +00:00
1. Pass `react-native-firebase` App instance into `reactReduxFirebase` when creating store:
2017-08-31 00:59:38 +00:00
**createStore.js**
2017-08-28 22:54:56 +00:00
```js
import { applyMiddleware, compose, createStore } from 'redux';
import RNFirebase from 'react-native-firebase';
2017-08-31 07:24:43 +00:00
import { getFirebase, reactReduxFirebase } from 'react-redux-firebase';
import thunk from 'redux-thunk';
2017-08-31 00:59:38 +00:00
import makeRootReducer from './reducers';
2017-08-28 22:54:56 +00:00
const reactNativeFirebaseConfig = {
debug: true
};
// for more config options, visit http://docs.react-redux-firebase.com/history/v2.0.0/docs/api/compose.html
const reduxFirebaseConfig = {
userProfile: 'users', // save users profiles to 'users' collection
2017-08-31 00:59:38 +00:00
};
2017-08-28 22:54:56 +00:00
2017-08-31 07:24:43 +00:00
export default (initialState = { firebase: {} }) => {
2017-08-31 00:59:38 +00:00
// initialize firebase
const firebase = RNFirebase.initializeApp(reactNativeFirebaseConfig);
const store = createStore(
makeRootReducer(),
2017-08-31 07:29:23 +00:00
initialState,
2017-08-31 00:59:38 +00:00
compose(
2017-08-31 07:24:43 +00:00
reactReduxFirebase(firebase, reduxFirebaseConfig), // pass initialized react-native-firebase app instance
2017-08-31 08:17:44 +00:00
// applyMiddleware(...middleware) // if using middleware
2017-08-31 00:59:38 +00:00
)
2017-08-31 07:24:43 +00:00
);
return store;
};
2017-08-31 00:59:38 +00:00
```
2017-08-31 07:29:23 +00:00
2017-08-31 07:24:43 +00:00
1. Wrap in `Provider` from `react-redux` :
2017-08-31 00:59:38 +00:00
**index.js**
```js
import React from 'react';
import { Provider } from 'react-redux';
import createStore from './createStore';
2017-08-31 08:17:44 +00:00
import Home from './Home';
2017-08-31 00:59:38 +00:00
// Store Initialization
const initialState = { firebase: {} };
2017-08-31 07:24:43 +00:00
let store = createStore(initialState);
2017-08-31 00:59:38 +00:00
2017-08-31 07:24:43 +00:00
const Main = () => (
2017-08-31 00:59:38 +00:00
< Provider store = {store} >
2017-08-31 08:17:44 +00:00
< Home / >
2017-08-31 00:59:38 +00:00
< / Provider >
);
2017-08-31 07:24:43 +00:00
export default Main;
2017-08-28 22:54:56 +00:00
```
2017-08-31 07:29:23 +00:00
1. Then you can use the `firebaseConnect` HOC to wrap your components. It makes it easy to set listeners which gather data from Firebase and place it into redux:
2017-08-28 22:54:56 +00:00
2017-08-31 07:24:43 +00:00
**Home.js**
2017-08-28 22:54:56 +00:00
```js
2017-08-31 07:24:43 +00:00
import React from 'react';
2017-08-28 22:54:56 +00:00
import { compose } from 'redux';
2017-08-31 07:24:43 +00:00
import { connect } from 'react-redux';
import { isLoaded, isEmpty, firebaseConnect } from 'react-redux-firebase';
import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';
import NewTodo from './NewTodo';
import Todos from './Todos';
2017-08-31 00:59:38 +00:00
2017-08-31 07:24:43 +00:00
class Home extends React.Component {
state = {
text: null
}
completeTodo = (key, todo) => {
return this.props.firebase.update(`todos/${key}`, { done: !todo.done })
}
2017-08-31 00:59:38 +00:00
2017-08-31 07:24:43 +00:00
addTodo = () => {
const { text } = this.state;
return this.props.firebase.push('todos', { text, completed: false });
2017-08-28 22:54:56 +00:00
}
2017-08-31 07:24:43 +00:00
render() {
const { todos } = this.props;
2017-08-31 00:59:38 +00:00
return (
2017-08-31 07:24:43 +00:00
< View >
< Text > Todos< / Text >
< NewTodo
onNewTouch={this.addTodo}
newValue={this.state.text}
onInputChange={(v) => this.setState({text: v})}
/>
{
!isLoaded(todos)
? < ActivityIndicator size = "large" style = {{ marginTop: 100 } } / >
: null
}
{
isLoaded(todos) & & !isEmpty(todos)
?
< Todos
todos={todos}
onItemTouch={this.completeTodo}
/>
:
< View style = {styles.container} >
< Text > No Todos Found< / Text >
< / View >
}
< / View >
);
2017-08-28 22:54:56 +00:00
}
}
2017-08-31 00:59:38 +00:00
export default compose(
2017-08-28 22:54:56 +00:00
firebaseConnect([
2017-08-31 07:24:43 +00:00
// create listener for firebase data -> redux
{ path: 'todos', queryParams: ['limitToLast=15'] }
2017-08-28 22:54:56 +00:00
]),
2017-08-31 07:24:43 +00:00
connect((state) => ({
// todos: state.firebase.data.todos, // todos data object from redux -> props.todos
todos: state.firebase.ordered.todos, // todos ordered array from redux -> props.todos
}))
)(Home);
2017-08-28 22:54:56 +00:00
```
2017-08-31 07:24:43 +00:00
**Todos.js**
2017-08-31 00:59:38 +00:00
```js
2017-08-31 07:24:43 +00:00
import React from 'react'
import {
View,
Text,
StyleSheet,
FlatList,
TouchableHighlight
} from 'react-native';
const Todos = ({ todos, onItemTouch }) => (
< FlatList
data={todos.reverse()}
renderItem={({ item: { key, value } }) => (
< TouchableHighlight onPress = {() = > onItemTouch(key, value)}>
< View >
< Text > {value.text}< / Text >
< Text > Done: {value.done === true ? 'True' : 'False'}< / Text >
< / View >
< / TouchableHighlight >
)}
/>
)
export default Todos;
2017-08-28 22:54:56 +00:00
```
2017-08-31 07:24:43 +00:00
Notice how `connect` is still used to get data out of `redux` since `firebaseConnect` only loads data **into** redux.
2017-08-28 22:54:56 +00:00
2017-08-31 07:34:04 +00:00
Full source with styling available [in the react-native-firebase example for react-redux-firebase ](https://github.com/prescottprue/react-redux-firebase/tree/v2.0.0/examples/complete/react-native-firebase )
2017-08-28 22:54:56 +00:00
For more details, please visit [`react-redux-firebase`'s react-native section ](http://docs.react-redux-firebase.com/history/v2.0.0/docs/recipes/react-native.html#native-modules ).
2017-08-26 23:00:08 +00:00
2017-08-31 08:17:44 +00:00
#### Thunks
`react-redux-firebase` provides the `getFirebase` helper for easy access to Firebase helper methods. Using this feature is as easy as passing it in while creating your store:
```js
const middleware = [
// make getFirebase available in third argument of thunks
thunk.withExtraArgument({ getFirebase }),
];
const store = createStore(
makeRootReducer(),
initialState,
compose(
reactReduxFirebase(firebase, reduxFirebaseConfig),
applyMiddleware(...middleware) // pass in middleware
)
);
```
2017-08-26 23:00:08 +00:00
## Standalone Integration
2017-07-06 10:08:25 +00:00
Although the following example works for a basic redux setup, it may differ when integrating with other redux middleware.
Imagine a simple TODO app, with redux we're able to abstract the Firebase logic out of components which allows for greater
testability and maintainability.
?> We use [`redux-thunk` ](https://github.com/gaearon/redux-thunk ) to provide async actions.
### Action Creators
```js
// Actions
export const subscribe = () => {
return (dispatch) => {
firebase.database().ref('todos').on('value', (snapshot) => {
const todos = [];
2017-08-26 05:48:26 +00:00
2017-07-06 10:08:25 +00:00
snapshot.forEach((childSnapshot) => {
todos.push({
id: childSnapshot.key,
...(childSnapshot.val()),
})
})
2017-08-26 05:48:26 +00:00
2017-07-06 10:08:25 +00:00
dispatch({
type: 'TODO_UPDATE',
todos,
})
})
}
}
// Methods
export const addTodo = text => {
firebase.database().ref('todos').push({
text,
visible: true,
})
}
export const completeTodo = id => {
firebase.database().ref(`todos/${id}`).update({
visible: false,
})
}
```
Instead of creating multiple actions which the reducers handle, we instead subscribe to the database ref and on any changes,
send a single action for the reducers to handle with the data which is constantly updating.
### Reducers
Our reducer now becomes really simple, as we're able to simply update the reducers state with whatever data has been returned
from our Firebase subscription.
```js
const todos = (state = {}, action) => {
switch (action.type) {
case 'TODO_UPDATE':
return { ...action.todos };
}
2017-08-26 05:48:26 +00:00
2017-07-06 10:08:25 +00:00
return state;
}
export default todos;
```
### Component
We can now easily subscribe to the todos in redux state and get live updates when Firebase updates.
```js
import React from 'react';
import { FlatList } from 'react-native';
import { connect } from 'react-redux';
import { subscribe, addTodo, completeTodo } from '../actions/TodoActions.js';
...
class Todos extends React.Component {
componentDidMount() {
this.props.dispatch(
subscribe()
);
}
2017-08-26 05:48:26 +00:00
2017-07-06 10:08:25 +00:00
onComplete = (id) => {
this.props.dispatch(
completeTodo(id)
);
};
2017-08-26 05:48:26 +00:00
2017-07-06 10:08:25 +00:00
onAdd = (text) => {
this.props.dispatch(
addTodo(text)
);
};
2017-08-26 05:48:26 +00:00
render() {
2017-07-06 10:08:25 +00:00
return (
< FlatList
data={this.props.todos}
...
/>
);
}
}
function mapStateToProps(state) {
return {
todos: state.todos,
};
}
export default connect(mapStateToProps)(Todos);
```