Skip to content

Instantly share code, notes, and snippets.

@lukekarrys
Last active December 7, 2016 17:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lukekarrys/a71aecac0426e86643b4 to your computer and use it in GitHub Desktop.
Save lukekarrys/a71aecac0426e86643b4 to your computer and use it in GitHub Desktop.
react-router-redux bug

react-router-redux bug

Linked to from reactjs/react-router-redux#244 and answered on SO.

Update I got this working by switching Page.js to dispatch its actions from componentDidUpdate instead of componentWillReceiveProps. See this revision for the full diff.

The current code in the gist still errors, but if you swap out componentWillReceiveProps for componentDidUpdate it works.

Error This should cause an error like this to appear in the console:

Uncaught Invariant Violation: findComponentRoot(..., .0.4.3): Unable to find element. This probably means the DOM was unexpectedly mutated (e.g., by the browser), usually due to forgetting a <tbody> when using tables, nesting tags like <form>, <p>, or <a>, or using non-SVG elements in an <svg> parent. Try inspecting the child nodes of the element with React ID ``.
{
"presets": ["react", "es2015"]
}
*.log
node_modules
dist
<!DOCTYPE html>
<html>
<head>
<title>react-router-redux bug</title>
<meta charset="utf8"/>
</head>
<body>
<div id="mount"></div>
<script src="dist/bundle.js"></script>
</body>
</html>
import React from 'react'
import { render } from 'react-dom'
import { Router, Route, browserHistory, Link } from 'react-router'
import { Provider } from 'react-redux'
import { syncHistoryWithStore, routerReducer } from 'react-router-redux'
import { createStore, applyMiddleware, combineReducers } from 'redux'
import thunk from 'redux-thunk'
import createLogger from 'redux-logger';
import Page from './Page'
const middleware = [ thunk, createLogger() ]
const dataReducer = (state = { syncing: false }, { type }) => {
if (type === 'SYNC') return { syncing: true }
if (type === 'DONE') return { syncing: false }
return state
}
const App = React.createClass({
render() {
return (
<div>
<Link to="/page/1">Link 1</Link> - <Link to="/page/2">Link 2</Link>
<p>Navigate to Link 1 and then Link 2 with the console open</p>
{this.props.children}
<p><br/><br/><a href='/' onClick={(e) => window.location = '/'}>reload</a></p>
</div>
)
}
})
const store = applyMiddleware(...middleware)(createStore)(combineReducers({
data: dataReducer,
routing: routerReducer
}))
// When using componentWillReceiveProps to dispatch an action in Page.js
// the error went away if this middleware was removed
const history = syncHistoryWithStore(browserHistory, store)
render(
<Provider store={store}>
<Router history={history}>
<Route path="/" component={App}>
<Route path="page/:param" component={Page} />
</Route>
</Router>
</Provider>,
document.getElementById('mount')
)
{
"name": "react-router-redux-bug",
"description": "react-router-redux-bug",
"version": "0.0.0",
"author": {
"name": "Luke Karrys",
"email": "luke@lukekarrys.com",
"url": "http://lukekarrys.com"
},
"dependencies": {
"react": "^0.14.7",
"react-dom": "^0.14.7",
"react-redux": "^4.4.0",
"react-router": "^2.0.0",
"react-router-redux": "^4.0.0",
"redux": "^3.3.1",
"redux-logger": "^2.6.0",
"redux-thunk": "^1.0.3"
},
"devDependencies": {
"babel-loader": "^6.2.3",
"babel-preset-es2015": "^6.5.0",
"babel-preset-react": "^6.5.0",
"http-server": "^0.9.0",
"webpack": "^1.12.14"
},
"license": "MIT",
"main": "webpack.config.js",
"private": true,
"scripts": {
"server": "http-server ./",
"start": "webpack && npm run server",
"watch": "webpack --watch"
}
}
import React from 'react'
import { connect } from 'react-redux'
const fetch = () => (dispatch) => {
dispatch({ type: 'SYNC' })
setTimeout(() => dispatch({ type: 'DONE' }), 100)
}
const Page = React.createClass({
componentDidMount() {
this.props.dispatch(fetch())
},
// Errors when dispatching from componentWillReceiveProps
// Comment out this and uncomment componentDidUpdate and it goes away
componentWillReceiveProps(nextProps) {
if (nextProps.params.param !== this.props.params.param) {
nextProps.dispatch(fetch())
}
},
// componentDidUpdate(prevProps) {
// if (prevProps.params.param !== this.props.params.param) {
// this.props.dispatch(fetch())
// }
// },
render() {
const { syncing, params: { param } } = this.props
if (syncing) return <h1>loading</h1>
let node = null
if (param === '2') {
// When using componentWillReceiveProps the error went away when
// if the onClick handler was removed
node = <span onClick={console.log.bind(console)}>link</span>
}
return <h1>param:{param} {node}</h1>
}
})
export default connect(state => ({ syncing: state.data.syncing }))(Page)
module.exports = {
entry: './main.js',
output: {
path: './dist',
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel',
exclude: /node_modules/
}
]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment