React-hot-loader
GuillaumeCisco opened this issue · 4 comments
Hey there, I'm experiencing a very very weird issue with the Link
component and react-hot loader.
I clearly don't know if this is an issue with redux-first-router-link
or with react-hot-loader
, but it looks like more with redux-first-router-link
as I experience this issue ONLY with the Link
component.
Issue : Everything before a <Link>
declaration hot reloads correctly, everything after does not. Like if when meeting a <Link/>
component, the hot reload stop refreshing my components, but it displays correctly in the console devtools part the components that should have been updated. Moreover, if I inspect my mapped file in the devtools, I can see correctly the file is updated, but nothing happen on the main window.
It really really surprised me and I think I still don't know where the problem come from.
So as a solution,I wrote my own version of the Link
component to debug it as I'm not fluent with flow
.
So my version of Link
looks like:
import React from 'react';
import {connect} from 'react-redux';
import handlePress from './handlePress';
const preventDefault = e => e && e.preventDefault && e.preventDefault();
const Link = ({
to,
href,
redirect,
replace,
tagName = 'a',
children,
onPress,
onClick,
down = false,
shouldDispatch = true,
target,
dispatch,
location,
...props
}) => {
const newTo = href || to; // href is deprecated and will be removed in next major version
const {routesMap} = location;
const url = newTo;
const handler = handlePress.bind(
null,
url,
routesMap,
onPress || onClick,
shouldDispatch,
target,
dispatch,
newTo,
replace || redirect,
);
const Root = tagName;
const localProps = {};
if (tagName === 'a' && url) {
localProps.href = url;
}
if (down && handler) {
localProps.onMouseDown = handler;
localProps.onTouchStart = handler;
}
if (target) {
localProps.target = target;
}
return (
<Root
onClick={(!down && handler) || preventDefault}
{...localProps}
{...props}
>
{children}
</Root>
);
};
const mapStateToProps = ({location}, ownProps) => ({location, ...ownProps});
// $FlowIgnore
export default connect(mapStateToProps)(Link);
handlePress.js
:
import { pathToAction, redirect, getOptions } from 'redux-first-router';
export default (
url,
routesMap,
onClick,
shouldDispatch,
target,
dispatch,
to,
dispatchRedirect,
e
) => {
let shouldGo = true;
if (onClick) {
shouldGo = onClick(e); // onClick can return false to prevent dispatch
shouldGo = typeof shouldGo === 'undefined' ? true : shouldGo;
}
const prevented = e.defaultPrevented;
if (!target && e && e.preventDefault && !isModified(e)) {
e.preventDefault();
}
if (
shouldGo &&
shouldDispatch &&
!target &&
!prevented &&
e.button === 0 &&
!isModified(e)
) {
const { querySerializer: serializer } = getOptions();
let action = isAction(to) ? to : pathToAction(url, routesMap, serializer);
action = dispatchRedirect ? redirect(action) : action;
dispatch(action);
}
}
const isAction = (to) => typeof to === 'object' && !Array.isArray(to);
const isModified = (e) => !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
As you can see, I don't use the store
and selectLocationState
for getting the location, but I connect my new Link
to redux for getting it.
Surprisingly, react-hot-loader works very well with this version.
Which suprises me even more, as I clearly do not understand why the flow version breaks... the flow.
Does someone ever experienced this issue?
I'm using :
"redux": "4.0.0",
"redux-actions": "2.4.0",
"redux-first-router": "1.9.15",
"redux-first-router-link": "1.4.2",
"react-hot-loader": "4.3.3",
I test this issue with the following snippet:
import Link from 'redux-first-router-link';
// import Link from './Link';
//
const Component = () =>
<h1>component</h1>;
const Routes = ({location}) => (
<div>
<Component/>
<Component/>
<Link to={{type: 'HOME'}}>test</Link>
<Component/>
<Component/>
</div>);
When I change the text inside the h1
, only the first two components are updated, not the two last.
If I change the text in the Link
component, it is not updated too.
Don't hesitate to ask for more information.
Thanks
Ok just found something even more interesting, I forgot something in my precedent snippet.
The correct one is:
import React from 'react';
import {connect} from 'react-redux';
import Link from 'redux-first-router-link';
// import Link from './Link';
//
const Component = () =>
<h1>component</h1>;
const Routes = ({location}) => (
<div>
<Component/>
<Component/>
<Link to={{type: 'HOME'}}>test</Link>
<Component/>
<Component/>
</div>);
const mapStateToProps = ({location}, ownProps) => ({location, ...ownProps});
export default connect(mapStateToProps)(Routes);
This does not work as explained just above.
But If I simply replace:
export default connect(mapStateToProps)(Routes);
by
export default Routes;
It does work well.
Looks like I have a conflict with the connect part of the redux-first-router-link, which use in its source code:
import { connect } from 'react-redux'
import type { Store } from 'redux'
import type { Connector } from 'react-redux'
...
const connector: Connector<OwnProps, Props> = connect()
// $FlowIgnore
export default connector(Link)
Any ideas on this issue?
Ok, after some tweaking in the redux-first-router-link code, I discovered that simply replacing in the dist/Link.js
file:
var connector = (0, _reactRedux.connect)();
// $FlowIgnore
exports.default = connector(Link);
by
exports.default = Link
make the Link component works correctly with react-hot-loader.
Why do we need to connect to redux the Link
component in the first place?
Ok I just found out the problem.
I'm using the autodll-webpack-plugin
and configured it like:
entry: {
reactVendors: [
'react',
'react-dom',
'react-emotion',
'emotion',
'react-redux',
'react-tap-event-plugin',
],
reduxVendors: [
'redux',
'redux-actions',
'redux-first-router',
'redux-reducers-injector',
'redux-saga',
'redux-sagas-injector',
],
commonVendors: [
'fastclick',
'history',
'react-helmet',
'recompose',
],
},
removing the react-redux
entry, implicitly from the dll generated js files makes it work.
I still don't understand why, but there is clearly something weird with the way react-redux is generated and used.
If someone has an idea and undesrtand why.
PS: I found out the solution, when simply importing the generated redux-first-router-link
in my project and see it working...
Lost 2 days on this.
Hey there, after a lot of research, I finally know what was the real guilty plugin:
new webpack.optimize.ModuleConcatenationPlugin()
Simply removing this plugin from my configuration made everything works correctly, with react-redux
or redux-first-router-link
not loaded in the dll list.
Hope it will help a lot of fellows ;)