Skip to content

Commit 724ff44

Browse files
author
Pavel Aksonov
committed
Implement correct replace, popTo actions, restore reducer
1 parent 8b58d15 commit 724ff44

File tree

7 files changed

+106
-56
lines changed

7 files changed

+106
-56
lines changed

.eslintrc.js

Lines changed: 22 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,59 @@
11
module.exports = {
22
extends: 'airbnb',
3-
plugins: [
4-
'react',
5-
'jest'
6-
],
3+
plugins: ['react', 'jest'],
74
env: {
8-
"jest/globals": true
5+
'jest/globals': true,
96
},
107
parser: 'babel-eslint',
118
rules: {
12-
"no-new-func": "warn",
13-
"jest/no-disabled-tests": "warn",
14-
"jest/no-focused-tests": "error",
15-
"jest/no-identical-title": "error",
16-
"jest/valid-expect": "error",
17-
18-
"react/forbid-prop-types": "warn",
19-
"react/prop-types": "off",
20-
"react/require-default-props": "off",
21-
"react/no-unused-prop-types": "off",
9+
'no-new-func': 'warn',
10+
'jest/no-disabled-tests': 'warn',
11+
'jest/no-focused-tests': 'error',
12+
'jest/no-identical-title': 'error',
13+
'jest/valid-expect': 'error',
2214

15+
'react/forbid-prop-types': 'warn',
16+
'react/prop-types': 'off',
17+
'react/require-default-props': 'off',
18+
'react/no-unused-prop-types': 'off',
19+
'no-param-reassign': 0,
2320
'no-console': 0,
2421
'new-cap': 0,
2522
'no-underscore-dangle': 0,
2623
'no-use-before-define': 0,
27-
'max-len': ["error", 180],
24+
'max-len': ['error', 180],
2825
'import/no-unresolved': [
2926
2,
3027
{
31-
ignore: [
32-
'^react$',
33-
'^react-native$',
34-
'^react-native/',
35-
],
28+
ignore: ['^react$', '^react-native$', '^react-native/'],
3629
},
3730
],
38-
"import/no-cycle": "warn",
39-
"import/no-self-import": "warn",
31+
'import/no-cycle': 'warn',
32+
'import/no-self-import': 'warn',
4033
'react/jsx-filename-extension': [
4134
1,
4235
{
43-
extensions: [
44-
'.js',
45-
'.jsx',
46-
],
36+
extensions: ['.js', '.jsx'],
4737
},
4838
],
4939
'import/no-extraneous-dependencies': [
5040
'error',
5141
{
52-
'devDependencies': true,
53-
}
42+
devDependencies: true,
43+
},
5444
],
5545
'no-bitwise': [
5646
'error',
5747
{
58-
'allow': ['^'],
48+
allow: ['^'],
5949
},
6050
],
61-
'no-restricted-syntax': [
62-
'error',
63-
'ForInStatement',
64-
'LabeledStatement',
65-
'WithStatement',
66-
],
51+
'no-restricted-syntax': ['error', 'ForInStatement', 'LabeledStatement', 'WithStatement'],
6752
},
6853
settings: {
6954
'import/resolver': {
7055
node: {
71-
extensions: [
72-
'.js',
73-
'.android.js',
74-
'.ios.js',
75-
],
56+
extensions: ['.js', '.android.js', '.ios.js'],
7657
},
7758
},
7859
node: true,

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-native-router-flux",
3-
"version": "4.0.0-beta.33",
3+
"version": "4.0.0-beta.40",
44
"description": "React Native Router using Flux architecture",
55
"repository": {
66
"type": "git",

src/Reducer.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import isEqual from 'lodash.isequal';
2+
import { NavigationActions, StackActions } from 'react-navigation';
3+
import navigationStore from './navigationStore';
4+
import * as ActionConst from './ActionConst';
5+
import { getActiveState, popPrevious } from './State';
6+
7+
export function reducer(state, action) {
8+
const { type, routeName } = action;
9+
if (type === ActionConst.POP_TO) {
10+
let nextScene = '';
11+
let newState = state;
12+
let currentState = state;
13+
while (newState && nextScene !== routeName) {
14+
newState = navigationStore.getStateForAction(StackActions.pop(), currentState);
15+
if (newState) {
16+
nextScene = getActiveState(newState).routeName;
17+
if (isEqual(currentState, newState)) {
18+
console.warn(`popTo called with an unknown routeName: ${routeName}`);
19+
break;
20+
}
21+
if (nextScene !== routeName) {
22+
currentState = newState;
23+
}
24+
}
25+
}
26+
return nextScene === routeName ? newState : state;
27+
}
28+
if (type === ActionConst.REPLACE) {
29+
const newState = navigationStore.getStateForAction(
30+
NavigationActions.navigate({
31+
routeName,
32+
params: action.params,
33+
}),
34+
state,
35+
);
36+
const res = popPrevious(newState, routeName);
37+
return res;
38+
}
39+
return navigationStore.getStateForAction(action, state) || state;
40+
}
41+
42+
export default function createReducer() {
43+
return reducer;
44+
}

src/Router.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ class App extends React.Component {
9292
}
9393

9494
const Router = ({
95-
sceneStyle, onStateChange, scenes, uriPrefix, navigator, getSceneStyle, children, onDeepLink, wrapBy, ...props
95+
createReducer, sceneStyle, onStateChange, scenes, uriPrefix, navigator, getSceneStyle, children, onDeepLink, wrapBy, ...props
9696
}) => {
9797
const data = { ...props };
9898
if (getSceneStyle) {
@@ -102,6 +102,8 @@ const Router = ({
102102
data.cardStyle = sceneStyle;
103103
}
104104
const AppNavigator = scenes || navigator || navigationStore.create(children, data, wrapBy);
105+
navigationStore.reducer = createReducer && createReducer(props);
106+
navigationStore.setCustomReducer(AppNavigator);
105107
if (onStateChange) {
106108
navigationStore.onStateChange = onStateChange;
107109
}
@@ -114,6 +116,7 @@ Router.propTypes = {
114116
wrapBy: PropTypes.func,
115117
getSceneStyle: PropTypes.func,
116118
sceneStyle: ViewPropTypes.style,
119+
createReducer: PropTypes.func,
117120
children: PropTypes.element,
118121
uriPrefix: PropTypes.string,
119122
onDeepLink: PropTypes.func,

src/State.js

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,21 @@ export function getActiveState(param, parent) {
3030
return getActiveState(state.routes[state.index], { ...state, parent });
3131
}
3232

33+
export function getParent(state, routeName, parent) {
34+
if (state.routeName === routeName) {
35+
return parent;
36+
}
37+
if (!state.routes) {
38+
return null;
39+
}
40+
for (let i = 0; i < state.routes.length; i += 1) {
41+
const res = getParent(state.routes[i], routeName, state);
42+
if (res) {
43+
return res;
44+
}
45+
}
46+
return null;
47+
}
3348
export function inject(state, key, index, routes) {
3449
if (!state.routes) {
3550
return state;
@@ -43,13 +58,11 @@ export function inject(state, key, index, routes) {
4358
return { ...state, routes: state.routes.map(x => inject(x, key, index, routes)) };
4459
}
4560

46-
export function popPrevious(state) {
47-
const activeState = getActiveState(state);
48-
if (activeState.parent && activeState.parent.index) {
49-
const {
50-
parent,
51-
parent: { key, index },
52-
} = activeState;
61+
export function popPrevious(state, routeName) {
62+
const parent = getParent(state, routeName);
63+
console.log('FOUND PARENT:', JSON.stringify(parent));
64+
const { key, index } = parent;
65+
if (index) {
5366
const routes = [...parent.routes.slice(0, index - 1), ...parent.routes.slice(index)];
5467
const newState = inject(state, key, index - 1, routes);
5568
return newState;

src/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as ActionConst from './ActionConst';
22
import Router from './Router';
3+
import Reducer from './Reducer';
34
import Scene from './Scene';
45
import Actions from './navigationStore';
56
import Modal from './Modal';
@@ -11,5 +12,5 @@ import Overlay from './Overlay';
1112
import pathParser from './pathParser';
1213

1314
export {
14-
ActionConst, Router, Scene, Actions, Modal, Lightbox, Stack, Drawer, Tabs, Overlay, pathParser,
15+
Reducer, ActionConst, Router, Scene, Actions, Modal, Lightbox, Stack, Drawer, Tabs, Overlay, pathParser,
1516
};

src/navigationStore.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
DrawerActions,
1111
} from 'react-navigation';
1212
import PropTypes from 'prop-types';
13+
import { reducer } from './Reducer';
1314
import * as ActionConst from './ActionConst';
1415
import { OnEnter, OnExit, assert } from './Util';
1516
import { LeftButton, RightButton, BackButton } from './NavBar';
@@ -471,6 +472,10 @@ const defaultSuccess = () => {};
471472
const defaultFailure = () => {};
472473

473474
class NavigationStore {
475+
getStateForAction = null;
476+
477+
reducer = null;
478+
474479
_navigator = null;
475480

476481
refs = {};
@@ -485,6 +490,11 @@ class NavigationStore {
485490

486491
onStateChange;
487492

493+
setCustomReducer = (Navigator) => {
494+
this.getStateForAction = Navigator.router.getStateForAction;
495+
Navigator.router.getStateForAction = (cmd, state) => (this.reducer ? this.reducer(state, cmd) : reducer(state, cmd));
496+
};
497+
488498
onNavigationStateChange = async (prevState, currentState, action) => {
489499
this.state = currentState;
490500
this.prevState = prevState;
@@ -827,12 +837,12 @@ class NavigationStore {
827837

828838
push = (routeName, data) => {
829839
const params = filterParam(data);
830-
this.dispatch(StackActions.push({ routeName, params }));
840+
this.dispatch({ type: NavigationActions.NAVIGATE, routeName, params });
831841
};
832842

833843
jump = (routeName, data) => {
834844
const params = filterParam(data);
835-
this.dispatch(NavigationActions.navigate({ routeName, params }));
845+
this.dispatch({ type: NavigationActions.NAVIGATE, routeName, params });
836846
};
837847

838848
drawerOpen = () => {
@@ -878,9 +888,7 @@ class NavigationStore {
878888

879889
replace = (routeName, data) => {
880890
const params = filterParam(data);
881-
this.dispatch(
882-
StackActions.replace({ routeName, params }),
883-
);
891+
this.dispatch({ type: ActionConst.REPLACE, routeName, params });
884892
};
885893

886894
reset = (routeName, data) => {

0 commit comments

Comments
 (0)