diff --git a/packages/react-navigation/docs/guides/Redux-Integration.md b/packages/react-navigation/docs/guides/Redux-Integration.md
index d1db71b1..6a8b9af2 100644
--- a/packages/react-navigation/docs/guides/Redux-Integration.md
+++ b/packages/react-navigation/docs/guides/Redux-Integration.md
@@ -1,17 +1,45 @@
# Redux Integration
### Overview For Redux Integration
-1. To handle your app's navigation state in redux, you can pass your own `navigation` prop to a navigator.
+1. To handle your app's navigation state in Redux, you can pass your own `navigation` prop to a navigator.
-2. Once you pass your own navigation prop to the navigator, the default [`navigation`](https://reactnavigation.org/docs/navigators/navigation-prop) prop gets destroyed. You will most probably pass the `navigation` prop's properties that you want to access. Normally [`state`](https://reactnavigation.org/docs/navigators/navigation-prop#state-The-screen's-current-stateroute) and [`dispatch`](https://reactnavigation.org/docs/navigators/navigation-prop#dispatch-Send-an-action-to-the-router) properties are passed to the navigator. You will learn how to pass those properties further in this guide. Since you have destroyed the default props, if you try to invoke something you have not explicitly passed down, it won't work. So, if you didn't pass `dispatch` to the navigator and only passes `state` than you can't access `dispatch` further in your Components.
+2. Once you pass your own navigation prop to the navigator, the default [`navigation`](https://reactnavigation.org/docs/navigators/navigation-prop) prop gets destroyed. You must construct your own `navigation` prop with [`state`](https://reactnavigation.org/docs/navigators/navigation-prop#state-The-screen's-current-stateroute), [`dispatch`](https://reactnavigation.org/docs/navigators/navigation-prop#dispatch-Send-an-action-to-the-router), and `addListener` properties.
-3. The `state` will be fed from the reducer assigned to handle navigation state and the `dispatch` will be redux's default `dispatch`. Thus you will be able to dispatch normal redux actions using `this.props.navigation.dispatch(ACTION)`, reducer will update the navigation state on the basis of dispatched action, the new navigation state will then be passed to the navigator.
+3. The `state` will be fed from the reducer assigned to handle navigation state and the `dispatch` will be Redux's default `dispatch`. Thus you will be able to dispatch normal redux actions using `this.props.navigation.dispatch(ACTION)`, reducer will update the navigation state on the basis of dispatched action, the new navigation state will then be passed to the navigator.
+
+4. A middleware is needed so that any events that mutate the navigation state properly trigger the event listeners.
### Details Regarding Redux Integration
-With redux, your app's state is defined by a reducer. Each navigation router effectively has a reducer, called `getStateForAction`. The following is a minimal example of how you might use navigators within a redux application:
+First, you need to add the `react-navigation-redux-helpers` package to your project.
+
+ ```bash
+ yarn add react-navigation-redux-helpers
+ ```
+
+ or
+
+ ```bash
+ npm install --save react-navigation-redux-helpers
+ ```
+
+With Redux, your app's state is defined by a reducer. Each navigation router effectively has a reducer, called `getStateForAction`. The following is a minimal example of how you might use navigators within a Redux application:
```es6
-import { addNavigationHelpers } from 'react-navigation';
+import {
+ StackNavigator,
+ addNavigationHelpers,
+} from 'react-navigation';
+import {
+ createStore,
+ applyMiddleware,
+ combineReducers,
+} from 'redux';
+import {
+ createReduxBoundAddListener,
+ createReactNavigationReduxMiddleware,
+} from 'react-navigation-redux-helpers';
+import { Provider, connect } from 'react-redux';
+import React from 'react';
const AppNavigator = StackNavigator(AppRouteConfigs);
@@ -29,12 +57,20 @@ const appReducer = combineReducers({
...
});
+// Note: createReactNavigationReduxMiddleware must be run before createReduxBoundAddListener
+const middleware = createReactNavigationReduxMiddleware(
+ "root",
+ state => state.nav,
+);
+const addListener = createReduxBoundAddListener("root");
+
class App extends React.Component {
render() {
return (
);
}
@@ -46,7 +82,10 @@ const mapStateToProps = (state) => ({
const AppWithNavigationState = connect(mapStateToProps)(App);
-const store = createStore(appReducer);
+const store = createStore(
+ appReducer,
+ applyMiddleware(middleware),
+);
class Root extends React.Component {
render() {
@@ -59,7 +98,7 @@ class Root extends React.Component {
}
```
-Once you do this, your navigation state is stored within your redux store, at which point you can fire navigation actions using your redux dispatch function.
+Once you do this, your navigation state is stored within your Redux store, at which point you can fire navigation actions using your Redux dispatch function.
Keep in mind that when a navigator is given a `navigation` prop, it relinquishes control of its internal state. That means you are now responsible for persisting its state, handling any deep linking, [Handling the Hardware Back Button in Android](#Handling-the-Hardware-Back-Button-in-Android), etc.
@@ -77,7 +116,7 @@ In this case, once you `connect` `AppNavigator` to Redux as is done in `AppWithN
## Full example
-There's a working example app with redux [here](https://github.com/react-community/react-navigation/tree/master/examples/ReduxExample) if you want to try it out yourself.
+There's a working example app with Redux [here](https://github.com/react-community/react-navigation/tree/master/examples/ReduxExample) if you want to try it out yourself.
## Mocking tests
@@ -129,9 +168,11 @@ class ReduxNavigation extends React.Component {
const { dispatch, nav } = this.props;
const navigation = addNavigationHelpers({
dispatch,
- state: nav
+ state: nav,
+ addListener,
});
return ;
}
}
+```
diff --git a/packages/react-navigation/examples/ReduxExample/App.js b/packages/react-navigation/examples/ReduxExample/App.js
index 0ddaa623..aa381200 100644
--- a/packages/react-navigation/examples/ReduxExample/App.js
+++ b/packages/react-navigation/examples/ReduxExample/App.js
@@ -1,21 +1,21 @@
-/**
- * @flow
- */
-
import React from 'react';
import { AppRegistry } from 'react-native';
import { Provider } from 'react-redux';
-import { createStore } from 'redux';
+import { createStore, applyMiddleware } from 'redux';
import AppReducer from './src/reducers';
import AppWithNavigationState from './src/navigators/AppNavigator';
+import { middleware } from './src/utils/redux';
+
+const store = createStore(
+ AppReducer,
+ applyMiddleware(middleware),
+);
class ReduxExampleApp extends React.Component {
- store = createStore(AppReducer);
-
render() {
return (
-
+
);
diff --git a/packages/react-navigation/examples/ReduxExample/package.json b/packages/react-navigation/examples/ReduxExample/package.json
index 719643af..690555e6 100644
--- a/packages/react-navigation/examples/ReduxExample/package.json
+++ b/packages/react-navigation/examples/ReduxExample/package.json
@@ -25,9 +25,10 @@
"prop-types": "^15.5.10",
"react": "16.2.0",
"react-native": "^0.52.0",
+ "react-navigation": "link:../..",
+ "react-navigation-redux-helpers": "^1.0.0",
"react-redux": "^5.0.6",
- "redux": "^3.7.2",
- "react-navigation": "link:../.."
+ "redux": "^3.7.2"
},
"devDependencies": {
"babel-jest": "^21.0.0",
diff --git a/packages/react-navigation/examples/ReduxExample/src/navigators/AppNavigator.js b/packages/react-navigation/examples/ReduxExample/src/navigators/AppNavigator.js
index e089289a..94f0a814 100644
--- a/packages/react-navigation/examples/ReduxExample/src/navigators/AppNavigator.js
+++ b/packages/react-navigation/examples/ReduxExample/src/navigators/AppNavigator.js
@@ -6,6 +6,7 @@ import { addNavigationHelpers, StackNavigator } from 'react-navigation';
import LoginScreen from '../components/LoginScreen';
import MainScreen from '../components/MainScreen';
import ProfileScreen from '../components/ProfileScreen';
+import { addListener } from '../utils/redux';
export const AppNavigator = StackNavigator({
Login: { screen: LoginScreen },
@@ -19,28 +20,6 @@ class AppWithNavigationState extends React.Component {
nav: PropTypes.object.isRequired,
};
- _actionEventSubscribers = new Set();
-
- _addListener = (eventName, handler) => {
- eventName === 'action' && this._actionEventSubscribers.add(handler);
- return {
- remove: () => {
- this._actionEventSubscribers.delete(handler);
- },
- };
- };
-
- componentDidUpdate(lastProps) {
- const lastState = lastProps.nav;
- this._actionEventSubscribers.forEach(subscriber => {
- subscriber({
- lastState: lastProps.nav,
- state: this.props.nav,
- action: this.props.lastAction,
- });
- });
- }
-
render() {
const { dispatch, nav } = this.props;
return (
@@ -48,7 +27,7 @@ class AppWithNavigationState extends React.Component {
navigation={addNavigationHelpers({
dispatch,
state: nav,
- addListener: this._addListener,
+ addListener,
})}
/>
);
@@ -57,7 +36,6 @@ class AppWithNavigationState extends React.Component {
const mapStateToProps = state => ({
nav: state.nav,
- lastAction: state.lastAction,
});
export default connect(mapStateToProps)(AppWithNavigationState);
diff --git a/packages/react-navigation/examples/ReduxExample/src/reducers/index.js b/packages/react-navigation/examples/ReduxExample/src/reducers/index.js
index 2a8ef8c0..1ece9bcd 100644
--- a/packages/react-navigation/examples/ReduxExample/src/reducers/index.js
+++ b/packages/react-navigation/examples/ReduxExample/src/reducers/index.js
@@ -36,10 +36,6 @@ function nav(state = initialNavState, action) {
return nextState || state;
}
-function lastAction(state = null, action) {
- return action;
-}
-
const initialAuthState = { isLoggedIn: false };
function auth(state = initialAuthState, action) {
@@ -54,7 +50,6 @@ function auth(state = initialAuthState, action) {
}
const AppReducer = combineReducers({
- lastAction,
nav,
auth,
});
diff --git a/packages/react-navigation/examples/ReduxExample/src/utils/redux.js b/packages/react-navigation/examples/ReduxExample/src/utils/redux.js
new file mode 100644
index 00000000..643e7dbd
--- /dev/null
+++ b/packages/react-navigation/examples/ReduxExample/src/utils/redux.js
@@ -0,0 +1,15 @@
+import {
+ createReactNavigationReduxMiddleware,
+ createReduxBoundAddListener,
+} from 'react-navigation-redux-helpers';
+
+const middleware = createReactNavigationReduxMiddleware(
+ "root",
+ state => state.nav,
+);
+const addListener = createReduxBoundAddListener("root");
+
+export {
+ middleware,
+ addListener,
+};
diff --git a/packages/react-navigation/examples/ReduxExample/yarn.lock b/packages/react-navigation/examples/ReduxExample/yarn.lock
index d1174913..992fef1f 100644
--- a/packages/react-navigation/examples/ReduxExample/yarn.lock
+++ b/packages/react-navigation/examples/ReduxExample/yarn.lock
@@ -5231,6 +5231,12 @@ react-native@^0.52.0:
xmldoc "^0.4.0"
yargs "^9.0.0"
+react-navigation-redux-helpers@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/react-navigation-redux-helpers/-/react-navigation-redux-helpers-1.0.0.tgz#baca0d080c6d486a06cbf1d54056794e33d22cca"
+ dependencies:
+ invariant "^2.2.2"
+
"react-navigation@link:../..":
version "0.0.0"
uid ""