redux-connect
Advanced tools
Comparing version 6.0.0 to 7.0.0
@@ -6,2 +6,6 @@ 'use strict'; | ||
var _jsx2 = require('babel-runtime/helpers/jsx'); | ||
var _jsx3 = _interopRequireDefault(_jsx2); | ||
var _extends2 = require('babel-runtime/helpers/extends'); | ||
@@ -31,6 +35,10 @@ | ||
var _RouterContext = require('react-router/lib/RouterContext'); | ||
var _Route = require('react-router/Route'); | ||
var _RouterContext2 = _interopRequireDefault(_RouterContext); | ||
var _Route2 = _interopRequireDefault(_Route); | ||
var _renderRoutes = require('react-router-config/renderRoutes'); | ||
var _renderRoutes2 = _interopRequireDefault(_renderRoutes); | ||
var _utils = require('../helpers/utils'); | ||
@@ -51,3 +59,3 @@ | ||
_this.state = { | ||
propsToShow: _this.isLoaded() ? props : null | ||
previousLocation: _this.isLoaded() ? null : props.location | ||
}; | ||
@@ -71,4 +79,6 @@ | ||
AsyncConnect.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) { | ||
var navigated = this.props.location !== nextProps.location; | ||
// Allow a user supplied function to determine if an async reload is necessary | ||
if (this.props.reloadOnPropsChange(this.props, nextProps)) { | ||
if (navigated && this.props.reloadOnPropsChange(this.props, nextProps)) { | ||
this.loadAsyncData(nextProps); | ||
@@ -78,6 +88,2 @@ } | ||
AsyncConnect.prototype.shouldComponentUpdate = function shouldComponentUpdate(nextProps, nextState) { | ||
return this.state.propsToShow !== nextState.propsToShow; | ||
}; | ||
AsyncConnect.prototype.componentWillUnmount = function componentWillUnmount() { | ||
@@ -95,4 +101,7 @@ this.mounted = false; | ||
var store = this.context.store; | ||
var loadResult = (0, _utils.loadAsyncConnect)((0, _extends3.default)({}, props, { store: store })); | ||
this.setState({ previousLocation: this.props.location }); | ||
// TODO: think of a better solution to a problem? | ||
@@ -108,3 +117,3 @@ this.loadDataCounter += 1; | ||
if (_this2.loadDataCounter === loadDataCounterOriginal && _this2.mounted !== false) { | ||
_this2.setState({ propsToShow: props }); | ||
_this2.setState({ previousLocation: null }); | ||
} | ||
@@ -120,5 +129,16 @@ | ||
AsyncConnect.prototype.render = function render() { | ||
var propsToShow = this.state.propsToShow; | ||
var _this3 = this; | ||
return propsToShow && this.props.render(propsToShow); | ||
var previousLocation = this.state.previousLocation; | ||
var _props = this.props, | ||
location = _props.location, | ||
_render = _props.render; | ||
return (0, _jsx3.default)(_Route2.default, { | ||
location: previousLocation || location, | ||
render: function render() { | ||
return _render(_this3.props); | ||
} | ||
}); | ||
}; | ||
@@ -137,6 +157,8 @@ | ||
}, | ||
render: function render(props) { | ||
return _react2.default.createElement(_RouterContext2.default, props); | ||
render: function render(_ref) { | ||
var routes = _ref.routes; | ||
return (0, _renderRoutes2.default)(routes); | ||
} | ||
}; | ||
exports.default = AsyncConnect; |
@@ -7,2 +7,6 @@ 'use strict'; | ||
var _withRouter = require('react-router/withRouter'); | ||
var _withRouter2 = _interopRequireDefault(_withRouter); | ||
var _AsyncConnect = require('../components/AsyncConnect'); | ||
@@ -12,2 +16,4 @@ | ||
exports.default = (0, _reactRedux.connect)(null, { beginGlobalLoad: _store.beginGlobalLoad, endGlobalLoad: _store.endGlobalLoad })(_AsyncConnect.AsyncConnect); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
exports.default = (0, _withRouter2.default)((0, _reactRedux.connect)(null, { beginGlobalLoad: _store.beginGlobalLoad, endGlobalLoad: _store.endGlobalLoad })(_AsyncConnect.AsyncConnect)); |
@@ -29,6 +29,5 @@ 'use strict'; | ||
var key = item.key; | ||
if (!key) { | ||
return item; | ||
} | ||
if (!key) return item; | ||
return (0, _extends4.default)({}, item, { | ||
@@ -35,0 +34,0 @@ promise: function promise(options) { |
@@ -5,2 +5,6 @@ 'use strict'; | ||
var _extends2 = require('babel-runtime/helpers/extends'); | ||
var _extends3 = _interopRequireDefault(_extends2); | ||
var _objectWithoutProperties2 = require('babel-runtime/helpers/objectWithoutProperties'); | ||
@@ -25,6 +29,10 @@ | ||
exports.filterAndFlattenComponents = filterAndFlattenComponents; | ||
exports.filterAndLayerComponents = filterAndLayerComponents; | ||
exports.filterComponents = filterComponents; | ||
exports.loadAsyncConnect = loadAsyncConnect; | ||
exports.loadOnServer = loadOnServer; | ||
var _matchRoutes = require('react-router-config/matchRoutes'); | ||
var _matchRoutes2 = _interopRequireDefault(_matchRoutes); | ||
var _store = require('../store'); | ||
@@ -51,6 +59,7 @@ | ||
var length = iterable.length; | ||
var results = new Array(length); | ||
var i = 0; | ||
return _promise2.default.resolve().then(function iterateOverResults() { | ||
function iterateOverResults() { | ||
return iterator(iterable[i], i, iterable).then(function (result) { | ||
@@ -65,3 +74,5 @@ results[i] = result; | ||
}); | ||
}); | ||
} | ||
return iterateOverResults(); | ||
}; | ||
@@ -113,33 +124,18 @@ | ||
/** | ||
* Returns an array of an array of components on the same layers that are wrapped | ||
* Returns an array of components that are wrapped | ||
* with reduxAsyncConnect | ||
* @param {Array} components | ||
* @param {Array} branch | ||
* @return {Array} | ||
*/ | ||
function filterAndLayerComponents(components) { | ||
var layered = []; | ||
var l = components.length; | ||
function filterComponents(branch) { | ||
return branch.reduce(function (result, _ref) { | ||
var route = _ref.route, | ||
match = _ref.match; | ||
var _loop2 = function _loop2(i) { | ||
var component = components[i]; | ||
if ((typeof component === 'undefined' ? 'undefined' : (0, _typeof3.default)(component)) === 'object') { | ||
var keys = (0, _keys2.default)(component); | ||
var componentLayer = []; | ||
keys.forEach(function (key) { | ||
if (component[key] && component[key].reduxAsyncConnect) { | ||
componentLayer.push(component[key]); | ||
} | ||
}); | ||
if (componentLayer.length > 0) { | ||
layered.push(componentLayer); | ||
} | ||
} else if (component && component.reduxAsyncConnect) { | ||
layered.push([component]); | ||
if (route.component && route.component.reduxAsyncConnect) { | ||
result.push([route.component, { route: route, match: match }]); | ||
} | ||
}; | ||
for (var i = 0; i < l; i += 1) { | ||
_loop2(i); | ||
} | ||
return layered; | ||
return result; | ||
}, []); | ||
} | ||
@@ -150,16 +146,18 @@ | ||
* and loads data | ||
* @param {Object} data.components | ||
* @param {Object} data.routes - static route configuration | ||
* @param {String} data.location - location object e.g. { pathname, query, ... } | ||
* @param {Function} [data.filter] - filtering function | ||
* @return {Promise} | ||
*/ | ||
function loadAsyncConnect(_ref) { | ||
var _ref$components = _ref.components, | ||
components = _ref$components === undefined ? [] : _ref$components, | ||
_ref$filter = _ref.filter, | ||
filter = _ref$filter === undefined ? function () { | ||
function loadAsyncConnect(_ref2) { | ||
var location = _ref2.location, | ||
_ref2$routes = _ref2.routes, | ||
routes = _ref2$routes === undefined ? [] : _ref2$routes, | ||
_ref2$filter = _ref2.filter, | ||
filter = _ref2$filter === undefined ? function () { | ||
return true; | ||
} : _ref$filter, | ||
rest = (0, _objectWithoutProperties3.default)(_ref, ['components', 'filter']); | ||
} : _ref2$filter, | ||
rest = (0, _objectWithoutProperties3.default)(_ref2, ['location', 'routes', 'filter']); | ||
var layered = filterAndLayerComponents(components); | ||
var layered = filterComponents((0, _matchRoutes2.default)(routes, location.pathname)); | ||
@@ -172,36 +170,35 @@ if (layered.length === 0) { | ||
// cycle | ||
return mapSeries(layered, function (componentArr) { | ||
if (componentArr.length === 0) { | ||
return mapSeries(layered, function (_ref3) { | ||
var component = _ref3[0], | ||
routeParams = _ref3[1]; | ||
if (component == null) { | ||
return _promise2.default.resolve(); | ||
} | ||
// Collect the results of each component on current layer. | ||
// Collect the results of each component | ||
var results = []; | ||
var asyncItemsArr = []; | ||
var asyncItems = component.reduxAsyncConnect; | ||
asyncItemsArr.push.apply(asyncItemsArr, asyncItems); | ||
var _loop3 = function _loop3(i) { | ||
var component = componentArr[i]; | ||
var asyncItems = component.reduxAsyncConnect; | ||
asyncItemsArr.push.apply(asyncItemsArr, asyncItems); | ||
// get array of results | ||
results.push.apply(results, asyncItems.reduce(function (itemsResults, item) { | ||
if (filter(item, component)) { | ||
var promiseOrResult = item.promise((0, _extends3.default)({}, rest, routeParams, { | ||
location: location, | ||
routes: routes | ||
})); | ||
// get array of results | ||
results.push.apply(results, asyncItems.reduce(function (itemsResults, item) { | ||
if (filter(item, component)) { | ||
var promiseOrResult = item.promise(rest); | ||
if (isPromise(promiseOrResult)) { | ||
promiseOrResult = promiseOrResult.catch(function (error) { | ||
return { error: error }; | ||
}); | ||
} | ||
itemsResults.push(promiseOrResult); | ||
if (isPromise(promiseOrResult)) { | ||
promiseOrResult = promiseOrResult.catch(function (error) { | ||
return { error: error }; | ||
}); | ||
} | ||
return itemsResults; | ||
}, [])); | ||
}; | ||
itemsResults.push(promiseOrResult); | ||
} | ||
for (var i = 0; i < componentArr.length; i += 1) { | ||
_loop3(i); | ||
} | ||
return itemsResults; | ||
}, [])); | ||
@@ -208,0 +205,0 @@ return _promise2.default.all(results).then(function (finalResults) { |
{ | ||
"name": "redux-connect", | ||
"version": "6.0.0", | ||
"version": "7.0.0", | ||
"description": "It allows you to request async data, store them in redux state and connect them to your react component.", | ||
@@ -8,3 +8,3 @@ "main": "lib/index.js", | ||
"type": "git", | ||
"url": "git+ssh://git@github.com/makeomatic/redux-connect.git" | ||
"url": "https://github.com/makeomatic/redux-connect" | ||
}, | ||
@@ -14,5 +14,6 @@ "scripts": { | ||
"lint": "eslint ./modules", | ||
"test": "npm run lint && jest", | ||
"pretest": "yarn lint", | ||
"test": "jest", | ||
"postversion": "npm publish && git push && git push --tags", | ||
"prepublish": "npm run lint && npm run build" | ||
"prepublish": "yarn lint && yarn build" | ||
}, | ||
@@ -26,3 +27,3 @@ "keywords": [ | ||
], | ||
"author": "Vitaly Aminev <v@makeomatic.ru>", | ||
"author": "Vitaly Aminev <v@makeomatic.ca>", | ||
"contributors": [ | ||
@@ -37,6 +38,7 @@ "Rodion Salnik (http://brocoders.com)" | ||
"peerDependencies": { | ||
"prop-types": "~15.x.x || ~16.x.x", | ||
"prop-types": "15.x.x || 16.x.x", | ||
"react": "16.x.x", | ||
"react-redux": "~5.x.x", | ||
"react-router": "~3.x.x" | ||
"react-redux": "5.x.x", | ||
"react-router": "4.x.x", | ||
"react-router-config": "1.x.x || ^1.0.0-beta" | ||
}, | ||
@@ -46,4 +48,4 @@ "devDependencies": { | ||
"babel-core": "^6.26.0", | ||
"babel-eslint": "^8.0.1", | ||
"babel-jest": "^21.2.0", | ||
"babel-eslint": "^8.1.2", | ||
"babel-jest": "^22.0.4", | ||
"babel-plugin-transform-runtime": "^6.15.0", | ||
@@ -54,12 +56,12 @@ "babel-preset-es2015": "^6.18.0", | ||
"babel-preset-stage-0": "^6.16.0", | ||
"bluebird": "^3.4.6", | ||
"enzyme": "^3.0.0", | ||
"enzyme-adapter-react-16": "^1.0.0", | ||
"eslint": "^4.8.0", | ||
"eslint-config-airbnb": "^15.1.0", | ||
"bluebird": "^3.5.1", | ||
"enzyme": "^3.3.0", | ||
"enzyme-adapter-react-16": "^1.1.1", | ||
"eslint": "^4.14.0", | ||
"eslint-config-airbnb": "^16.1.0", | ||
"eslint-plugin-import": "^2.7.0", | ||
"eslint-plugin-jsx-a11y": "5.x.x", | ||
"eslint-plugin-jsx-a11y": "6.0.3", | ||
"eslint-plugin-react": "^7.4.0", | ||
"immutable": "^3.8.1", | ||
"jest-cli": "^21.2.1", | ||
"jest-cli": "^22.0.4", | ||
"prop-types": "^15.6.0", | ||
@@ -70,7 +72,8 @@ "raf": "^3.3.2", | ||
"react-redux": "^5.0.6", | ||
"react-router": "3.x.x", | ||
"react-router": "4.x.x", | ||
"react-router-config": "^1.0.0 || ^1.0.0-beta", | ||
"react-test-renderer": "^16.0.0", | ||
"redux": "^3.7.2", | ||
"redux-immutable": "^4.0.0", | ||
"sinon": "^4.0.0" | ||
"sinon": "^4.1.3" | ||
}, | ||
@@ -90,3 +93,7 @@ "dependencies": { | ||
] | ||
} | ||
}, | ||
"files": [ | ||
"modules/", | ||
"lib/" | ||
] | ||
} |
147
README.MD
@@ -31,7 +31,8 @@ ReduxConnect for React Router | ||
```js | ||
import { Router, browserHistory } from 'react-router'; | ||
import BrowserRouter from 'react-router-dom/BrowserRouter' | ||
import renderRoutes from 'react-router-config/renderRoutes' | ||
import { ReduxAsyncConnect, asyncConnect, reducer as reduxAsyncConnect } from 'redux-connect' | ||
import React from 'react' | ||
import { hydrate } from 'react-dom' | ||
import { createStore, combineReducers } from 'redux'; | ||
import { createStore, combineReducers } from 'redux' | ||
@@ -41,3 +42,3 @@ // 1. Connect your data, similar to react-redux @connect | ||
key: 'lunch', | ||
promise: ({ params, helpers }) => Promise.resolve({ id: 1, name: 'Borsch' }) | ||
promise: ({ match: { params }, helpers }) => Promise.resolve({ id: 1, name: 'Borsch' }) | ||
}]) | ||
@@ -47,5 +48,9 @@ class App extends React.Component { | ||
// 2. access data as props | ||
const lunch = this.props.lunch | ||
const { lunch, route } = this.props | ||
return ( | ||
<div>{lunch.name}</div> | ||
<div> | ||
{lunch.name} | ||
{renderRoutes(route.routes)} | ||
</div> | ||
) | ||
@@ -55,11 +60,29 @@ } | ||
// 3. Connect redux async reducer | ||
const store = createStore(combineReducers({ reduxAsyncConnect }), window.__data); | ||
class Child extends React.component { | ||
render() { | ||
return ( | ||
<div>{'child component'}</div> | ||
) | ||
} | ||
} | ||
// 4. Render `Router` with ReduxAsyncConnect middleware | ||
const routes = [{ | ||
path: '/', | ||
component: App, | ||
routes: [{ | ||
path: '/child', | ||
exact: true, | ||
component: Child, | ||
}] | ||
}] | ||
// 2. Connect redux async reducer | ||
const store = createStore(combineReducers({ reduxAsyncConnect }), window.__data) | ||
// 3. Render `Router` with ReduxAsyncConnect middleware | ||
hydrate(( | ||
<Provider store={store} key="provider"> | ||
<Router render={(props) => <ReduxAsyncConnect {...props}/>} history={browserHistory}> | ||
<Route path="/" component={App}/> | ||
</Router> | ||
<BrowserRouter> | ||
<ReduxAsyncConnect routes={routes} helpers={helpers} /> | ||
</BrowserRouter> | ||
</Provider> | ||
@@ -73,23 +96,33 @@ ), el) | ||
import { renderToString } from 'react-dom/server' | ||
import { match, RoutingContext } from 'react-router' | ||
import StaticRouter from 'react-router/StaticRouter' | ||
import { ReduxAsyncConnect, loadOnServer, reducer as reduxAsyncConnect } from 'redux-connect' | ||
import createHistory from 'history/lib/createMemoryHistory'; | ||
import { Provider } from 'react-redux'; | ||
import { createStore, combineReducers } from 'redux'; | ||
import serialize from 'serialize-javascript'; | ||
import { parse as parseUrl } from 'url' | ||
import { Provider } from 'react-redux' | ||
import { createStore, combineReducers } from 'redux' | ||
import serialize from 'serialize-javascript' | ||
app.get('*', (req, res) => { | ||
const store = createStore(combineReducers({ reduxAsyncConnect })); | ||
const store = createStore(combineReducers({ reduxAsyncConnect })) | ||
const url = req.originalUrl || req.url | ||
const location = parseUrl(url) | ||
match({ routes, location: req.url }, (err, redirect, renderProps) => { | ||
// 1. load data | ||
loadOnServer({ ...renderProps, store }).then(() => { | ||
// 2. use `ReduxAsyncConnect` instead of `RoutingContext` and pass it `renderProps` | ||
// 1. load data | ||
loadOnServer({ store, location, routes, helpers }) | ||
.then(() => { | ||
const context = {} | ||
// 2. use `ReduxAsyncConnect` to render component tree | ||
const appHTML = renderToString( | ||
<Provider store={store} key="provider"> | ||
<ReduxAsyncConnect {...renderProps} /> | ||
<StaticRouter location={location} context={context}> | ||
<ReduxAsyncConnect routes={routes} helpers={helpers} /> | ||
</StaticRouter> | ||
</Provider> | ||
) | ||
// handle redirects | ||
if (context.url) { | ||
req.header('Location', context.url) | ||
return res.send(302) | ||
} | ||
@@ -100,3 +133,2 @@ // 3. render the Redux initial data into the server markup | ||
}) | ||
}) | ||
}) | ||
@@ -112,3 +144,5 @@ | ||
<!-- its a Redux initial data --> | ||
<script dangerouslySetInnerHTML={{__html: `window.__data=${serialize(store.getState())};`}} charSet="UTF-8"/> | ||
<script type="text/javascript"> | ||
window.__data=${serialize(store.getState())}; | ||
</script> | ||
</body> | ||
@@ -122,37 +156,2 @@ </html> | ||
## Usage with `applyRouterMiddleware` | ||
Thanks to @mmahalwy for a good usage example | ||
Pass custom `render` method to `ReduxAsyncConnect`, it can look like this: | ||
```js | ||
// on client | ||
const component = ( | ||
<Router | ||
render={(props) => ( | ||
<ReduxAsyncConnect | ||
{...props} | ||
helpers={{ client }} | ||
filter={item => !item.deferred} | ||
render={applyRouterMiddleware(useScroll())} | ||
/> | ||
)} | ||
history={history} | ||
routes={getRoutes(store)} | ||
/> | ||
); | ||
``` | ||
Basically what you do is instead of using render method like: | ||
```js | ||
const render = props => <RouterContext {...props} />; | ||
``` | ||
you use | ||
```js | ||
const render = applyRouterMiddleware(...middleware); | ||
``` | ||
## Usage with `ImmutableJS` | ||
@@ -176,28 +175,2 @@ | ||
**React Router Issue** | ||
While using the above immutablejs solution, an issue arose causing infinite recursion after firing | ||
off a react standard action. The recursion was caused because the `componentWillReceiveProps` method will attempt to resync with the server. Thus `componentWillReceiveProps -> resync with server -> changes props via reducer -> componentWillReceiveProps` | ||
The solution was to only resync with server on route changes. A `reloadOnPropsChange` prop is expose on the ReduxAsyncConnect component to allow customization of when a resync to the server should occur. | ||
Method signature `(props, nextProps) => bool` | ||
```js | ||
const reloadOnPropsChange = (props, nextProps) => { | ||
// reload only when path/route has changed | ||
return props.location.pathname !== nextProps.location.pathname; | ||
}; | ||
export const Root = ({ store, history }) => ( | ||
<Provider store={store} key="provider"> | ||
<Router render={(props) => <ReduxAsyncConnect {...props} | ||
reloadOnPropsChange={reloadOnPropsChange}/>} history={history}> | ||
{getRoutes(store)} | ||
</Router> | ||
</Provider> | ||
); | ||
``` | ||
## Comparing with other libraries | ||
@@ -204,0 +177,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
17
966
46050
7
30
203