Skip to content

Commit 2c8c4f3

Browse files
authored
Merge pull request #11 from metalabdesign/refactor-actions
Simplify actions and selectors.
2 parents 448c5d2 + dde7032 commit 2c8c4f3

5 files changed

Lines changed: 77 additions & 113 deletions

File tree

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
},
1616
"license": "CC0-1.0",
1717
"dependencies": {
18-
"core-js": "^2.4.1",
1918
"cuid": "^1.3.8",
2019
"fbjs": "^0.8.4",
2120
"hoist-non-react-statics": "^1.2.0"

src/action.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,30 @@
11
import cuid from 'cuid';
22

33
export const ADD_COMPONENT = '@@relocation/ADD_COMPONENT';
4+
export const SET_COMPONENT = '@@relocation/SET_COMPONENT';
5+
export const UPDATE_COMPONENT = '@@relocation/UPDATE_COMPONENT';
46
export const REMOVE_COMPONENT = '@@relocation/REMOVE_COMPONENT';
57
export const SET_ROUTE_COMPONENTS = '@@relocation/SET_ROUTE_COMPONENTS';
68

7-
export const addComponent = ({type, props, id = cuid()}) => ({
9+
export const addComponent = (type, props) => ({
810
type: ADD_COMPONENT,
11+
payload: {id: cuid(), type, props},
12+
});
13+
14+
export const setComponent = (type, id = type, props) => ({
15+
type: SET_COMPONENT,
916
payload: {id, type, props},
1017
});
1118

12-
export const removeComponent = (id) => ({type: REMOVE_COMPONENT, payload: id});
19+
export const updateComponent = (id, props) => ({
20+
type: UPDATE_COMPONENT,
21+
payload: {id, props},
22+
});
23+
24+
export const removeComponent = (id) => ({
25+
type: REMOVE_COMPONENT,
26+
payload: {id},
27+
});
1328

1429
export const setRouteComponents = (components) => ({
1530
type: SET_ROUTE_COMPONENTS,

src/connect.js

Lines changed: 23 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import {Component, createElement, PropTypes} from 'react';
22
import hoistStatics from 'hoist-non-react-statics';
33
import {connect} from 'react-redux';
44

5-
import {getMergedComponents} from './selector';
6-
import {removeComponent} from './action';
5+
import {getComponents} from './selector';
6+
import {removeComponent, updateComponent} from './action';
77
import {componentsShape, renderMapShape, getDisplayName} from './util';
88

99
/**
@@ -43,80 +43,36 @@ export default ({scope, ...defaultProps} = {}) => (WrappedComponent) => {
4343

4444
render() {
4545
const {components, renderMap} = this.props.___relocationState___;
46-
const {removeComponent} = this.props.___relocationDispatch___;
46+
const {
47+
removeComponent,
48+
updateComponent,
49+
} = this.props.___relocationDispatch___;
4750

4851
const inRenderMap = (component) =>
4952
typeof renderMap[component.type] === 'function';
5053

51-
const assignRender = (component) => ({
52-
...component,
53-
render: renderMap[component.type],
54-
});
55-
56-
const assignScope = (component) => ({...component, scope});
57-
58-
const assignRemoveHandler = (component) => {
59-
let removeHandler = null;
60-
61-
if (typeof component.remove === 'function') {
62-
// The component object remove property is already a function.
63-
// We don't want to override this behavior.
64-
removeHandler = component.remove;
65-
} else if (component.remove === undefined || component.remove) {
66-
// The component object does not have a `remove` property, or it has
67-
// a truthy value that is not a function. Either case indicates that
68-
// it should use the default remove handler.
69-
removeHandler = () => removeComponent(component.id);
70-
}
71-
72-
let pathRemoveHandler = null;
73-
74-
if (typeof component.removePath === 'string') {
75-
// Create a function that will change the history state when removing
76-
// the component.
77-
pathRemoveHandler = () => this.navigateToPath(component.removePath);
78-
}
79-
80-
if (pathRemoveHandler && removeHandler) {
81-
// A remove handler function and a
82-
return {
83-
...component,
84-
remove: () => {
85-
pathRemoveHandler();
86-
return removeHandler();
87-
},
88-
};
89-
}
90-
91-
if (pathRemoveHandler && !removeHandler) {
92-
return {
93-
...component,
94-
remove: pathRemoveHandler,
95-
};
96-
}
97-
98-
if (!pathRemoveHandler && removeHandler !== component.remove) {
99-
return {
100-
...component,
101-
remove: removeHandler,
102-
};
54+
const assign = (component) => {
55+
const result = {
56+
...component,
57+
render: renderMap[component.type],
58+
update: (props) => updateComponent(component.id, props),
59+
remove: typeof component.removePath === 'string'
60+
? () => this.navigateToPath(component.removePath)
61+
: () => removeComponent(component.id),
62+
};
63+
64+
if (scope) {
65+
result.scope = scope;
10366
}
10467

105-
// `!pathRemoveHandler && removeHandler === component.remove` is true.
106-
// This means `remove` was set and `removePath` was not set on the
107-
// component object. No modification is necessary.
108-
return component;
68+
return result;
10969
};
11070

11171
const currentComponents = components
11272
// Remove components not included in the render function map.
11373
.filter(inRenderMap)
114-
// Assign render functions.
115-
.map(assignRender)
116-
// Assign scope, if configured.
117-
.map(scope ? assignScope : (component) => component)
118-
// Assign remove handler functions.
119-
.map(assignRemoveHandler);
74+
// Assign render update and remove functions and scope if it is defined.
75+
.map(assign);
12076

12177
/* eslint-disable no-unused-vars */
12278
const {
@@ -155,7 +111,7 @@ export default ({scope, ...defaultProps} = {}) => (WrappedComponent) => {
155111
// Put everything in a ___relocationState___ namespace to avoid possible
156112
// conflict with existing props.
157113
___relocationState___: {
158-
components: getMergedComponents(state, selectorProps),
114+
components: getComponents(state, selectorProps),
159115
renderMap: components,
160116
},
161117
};
@@ -166,6 +122,7 @@ export default ({scope, ...defaultProps} = {}) => (WrappedComponent) => {
166122
// possible conflict with existing props.
167123
___relocationDispatch___: {
168124
removeComponent: (id) => dispatch(removeComponent(id)),
125+
updateComponent: (id, props) => dispatch(updateComponent(id, props)),
169126
},
170127
});
171128

src/reducer.js

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,33 @@
11
import {combineReducers} from 'redux';
22
import {
3-
REMOVE_COMPONENT,
43
ADD_COMPONENT,
4+
SET_COMPONENT,
5+
UPDATE_COMPONENT,
6+
REMOVE_COMPONENT,
57
SET_ROUTE_COMPONENTS,
68
} from './action';
79

8-
const createReducer = (type, initial) =>
9-
(state = initial, action) => action.type === type ? action.payload : state;
10-
1110
export default combineReducers({
1211
components: (state = [], action) => ({
13-
[ADD_COMPONENT]: (state, {payload}) => [...state, payload],
12+
[ADD_COMPONENT]: (state, {payload}) =>
13+
[...state, payload],
14+
15+
[SET_COMPONENT]: (state, {payload}) =>
16+
[...state.filter((item) => item.id !== payload.id), payload],
17+
18+
[UPDATE_COMPONENT]: (state, {payload}) =>
19+
state.map((item) => item.id === payload.id
20+
? {...item, props: {...item.props, ...payload.props}}
21+
: item
22+
),
23+
1424
[REMOVE_COMPONENT]: (state, {payload}) =>
15-
state.filter((item) => item.id !== payload),
25+
state.filter((item) => item.id !== payload.id),
26+
1627
}[action.type] || (() => state))(state, action),
17-
routeComponents: createReducer(SET_ROUTE_COMPONENTS, []),
28+
29+
routeComponents: (state = [], action) =>
30+
action.type === SET_ROUTE_COMPONENTS
31+
? action.payload
32+
: state,
1833
});

src/selector.js

Lines changed: 15 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,16 @@
1-
import findIndex from 'core-js/library/fn/array/find-index';
2-
31
export const getRelocation = (state, props) =>
4-
(props && props.getRelocationState) ?
5-
props.getRelocationState(state, props) :
6-
state.relocation;
7-
8-
export const getComponents = (state, props) =>
9-
getRelocation(state, props).components;
10-
11-
export const getRouteComponents = (state, props) =>
12-
getRelocation(state, props).routeComponents;
13-
14-
export const getMergedComponents = (state, props) =>
15-
[...getRouteComponents(state, props), ...getComponents(state, props)].
16-
reduce((components, component) => {
17-
const index = findIndex(components, ({id}) => id === component.id);
18-
19-
if (index === -1) {
20-
components.push(component);
21-
return components;
22-
}
23-
24-
const existing = components[index];
25-
26-
components[index] = {
27-
...existing,
28-
...component,
29-
...existing.props && component.props ? {
30-
props: {
31-
...existing.props,
32-
...component.props,
33-
},
34-
} : null,
35-
};
36-
37-
return components;
38-
}, []);
2+
(props && props.getRelocationState)
3+
? props.getRelocationState(state, props)
4+
: state.relocation;
5+
6+
export const getComponents = (state, props) => {
7+
const routeComponents = getRelocation(state, props).routeComponents;
8+
const components = getRelocation(state, props).components;
9+
10+
// Concat components and route components uniquely by id.
11+
return routeComponents
12+
.filter(({id: routeId}) =>
13+
!components.filter(({id}) => id === routeId).length
14+
)
15+
.concat(components);
16+
};

0 commit comments

Comments
 (0)