我遇到了一个问题,即制作一个将Routes放入容器的组件似乎会破坏路由器的反应。如果包含我的路由的组件是容器,则单击链接会更新URL但不会更改呈现的屏幕。如果我在URL更改后刷新页面或直接转到URL,则会呈现预期的视图。将容器更改回常规组件似乎可以解决此问题,但我希望将其保留为容器,以便我可以使用必要的操作。
路线坏了:
import React, { Component } from 'react'
import { Route } from 'react-router-dom'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import '../styles/App.css';
import Header from '../components/Header'
import Home from './Home'
import NewPost from './NewPost'
import { getAllPosts, getAllCategories } from '../actions'
class App extends Component {
componentDidMount() {
this.props.getAllPosts()
this.props.getAllCategories()
}
render() {
return (
<div className="App">
<Header />
<Route exact path="/" render={() => (
<Home />
)} />
<Route exact path="/post" render={() => (
<NewPost />
)} />
<Route path="/article" render={() => (
<div>article page</div>
)} />
</div>
);
}
}
const mapDispatchToProps = dispatch => (
bindActionCreators({
getAllPosts,
getAllCategories
}, dispatch)
)
export default connect(null, mapDispatchToProps)(App)
工作路线:
import React, { Component } from 'react'
import { Route } from 'react-router-dom'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import '../styles/App.css';
import Header from '../components/Header'
import Home from './Home'
import NewPost from './NewPost'
import { getAllPosts, getAllCategories } from '../actions'
export default class App extends Component {
componentDidMount() {
//this.props.getAllPosts()
//this.props.getAllCategories()
}
render() {
return (
<div className="App">
<Header />
<Route exact path="/" render={() => (
<Home />
)} />
<Route exact path="/post" render={() => (
<NewPost />
)} />
<Route path="/article" render={() => (
<div>article page</div>
)} />
</div>
);
}
}
const mapDispatchToProps = dispatch => (
bindActionCreators({
getAllPosts,
getAllCategories
}, dispatch)
)
// export default connect(null, mapDispatchToProps)(App)
任何对此的帮助都会受到大力赞赏,这让我疯狂!
编辑:为了清楚起见,我的index.js包含了BrowserRouter:
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, applyMiddleware, compose } from 'redux'
import { Provider } from 'react-redux'
import { BrowserRouter } from 'react-router-dom'
import thunk from 'redux-thunk'
// Middleware
import logger from 'redux-logger'
// Martyware
import rootReducer from './reducers'
import App from './containers/App';
import './styles/index.css';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore(
rootReducer,
composeEnhancers(
applyMiddleware(thunk),
applyMiddleware(logger)
)
)
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
, document.getElementById('root')
)
答案 0 :(得分:1)
有几件事情需要考虑,但简而言之,我认为您的问题正在发生,因为您的组件不会重新呈现,因为它不知道位置已更改。对此的快速解决方案是使用withRouter。
import { withRouter } from 'react-router';
...
export default withRouter(connect(null, mapDispatchToProps)(App));
现在,您的代码还有其他方面可以指出潜在的问题。 <Router />
不可见,因此我们只能假设它将您的组件包装在更高的级别。您没有将任何Redux状态映射到props,因此也不会导致重新渲染。
为什么需要使用路由器?
根据要求,我将尝试解释withRouter
的影响,但为此我需要了解React组件如何重新渲染。为了防止不断重新渲染,React会查看方法componentShouldUpdate
的结果,默认情况下比较这个组件的props(和state),如果它们发生了更改,则调用render
函数。如果他们没有改变它没有。
因此,当您的组件包含<Route />
(示例中为<App />
)时,无论位置是否发生更改,<App />
道具和状态都不会更改,并且这样,render
函数不再被调用。如果未再次调用,则不会要求与您的路径匹配的<Route />
重新呈现。简而言之,如果您的父组件没有重新渲染,它的子组件也不会重新渲染。这通常是为什么像Redux这样的工具存在的原因,能够更新树中的组件,而不必从父项中传递所有属性。
withRouter
所做的是它将属性location
(以及其他属性)添加到您的<App />
组件中,这样无论何时位置发生变化,道具都会有效地更改,因此强制重新渲染。您的组件是否包含在Redux的connect()
中不会影响此。
那么,为什么当你没有<App />
但是同一个组件中的所有东西都可以工作呢?好吧,那是因为您的主要组件重新渲染,因此render
中的每个组件都被询问是否也想重新渲染。
显然,使用withRouter
的效果不是很好,因为您可能会比您更喜欢重新渲染方式。但是,按照重新渲染完成最少工作量的方式组织此类组件是您的工作。您可能会使用依赖于自己的道具的子组件,而不是location
本身。
还有一些方法可以将React路由器与Redux一起使用,我认为你不需要withRouter
,但实际上Redux会导致你的道具发生变化,触发重新渲染。我没有看过这个,所以不要相信我的话。
答案 1 :(得分:0)
我发现这篇文章详细解释了这个问题https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/guides/blocked-updates.md
据我所知,connect阻止了组件的重新呈现,因为它以阻止重新呈现的方式调用shouldComponentUpdate。使用withRouter工作,但不是最有效的解决方案(https://github.com/ReactTraining/react-router/pull/5552#issuecomment-331502281)
我最终使用的解决方案是在index.js中通过无路径路径(它将始终匹配并因此呈现App)渲染App,这里是index.js中更新的渲染方法
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<Route component={App} />
</BrowserRouter>
</Provider>
, document.getElementById('root')
)
&#13;
我希望这对某人有用。很多,非常感谢FMCorz指出我正确的方向!