React-Router问题:点击Link更新URL但视图未刷新求助
你遇到的核心问题是:通过<Link>点击跳转至/deck/:deckId后,VisibleCards组件没有触发渲染,但手动刷新页面或直接输入URL时组件能正常工作。这是因为你目前的代码依赖store.subscribe(run)来手动重新渲染整个应用,而路由变化时如果Redux Store没有发生更新,这个订阅回调不会执行,导致React无法感知路由变化来更新对应组件。
具体解决方案
1. 移除手动的Store订阅与全局Render调用
你当前通过store.subscribe(run)每次Store变化就重新调用ReactDOM.render,这不符合React-Redux的最佳实践,而且会导致路由变化但Store未更新时,组件无法重新渲染。修改你的app.js代码如下:
// STORE const store = createStore(combineReducers(reducers)); const history = syncHistoryWithStore(createBrowserHistory(), store); function run(){ let state = store.getState(); console.log(state); ReactDOM.render( <Provider store={store}> <Router history={history}> <div className="holder"> <div className="sidebar"> <Route path='/' component={App}/> </div> <div className="VisibleCards"> <Route path='/deck/:deckId' component={VisibleCards}/> </div> </div> </Router> </Provider>,document.getElementById('root')); } // 只调用一次初始化渲染即可,React和React-Redux会自动处理后续更新 run(); // 保留全局方法 window.show = ()=>{store.dispatch(showAddDeck())}; window.hide = ()=>{store.dispatch(hideAddDeck())}; window.add = ()=>{store.dispatch(addDeck(new Date().toString()))};
2. 确认React Router与Redux的集成逻辑
你已经使用syncHistoryWithStore同步路由历史与Redux Store,这部分是正确的,但需要确保你的路由reducer(即reducers中的router reducer)配置正确——如果是旧版React Router,需使用react-router-redux提供的routerReducer;如果是新版React Router,直接使用官方组件/hooks即可,无需额外同步逻辑。
3. 验证组件的路由监听
你的VisibleCards组件已经用withRouter包裹了connect的结果,这能确保组件接收到路由相关的props(如match、location),当路由变化时,组件会自动接收到新props并重新渲染,这部分无需修改。
为什么这能解决问题?
- React-Redux的
<Provider>会让所有connect的组件自动监听Store变化,当Store更新时,相关组件会自动重新渲染,不需要手动调用ReactDOM.render。 - React Router会独立监听路由历史的变化,当通过
<Link>跳转时,它会更新路由状态并通知对应的<Route>组件重新渲染匹配的组件,无需依赖Redux Store的变化。
之前的问题正是因为路由变化时Store没有更新,store.subscribe(run)不会触发,导致整个应用没有重新渲染,VisibleCards组件也就不会被挂载/更新。移除手动订阅后,React Router可以独立处理路由变化的渲染,同时Redux的状态变化也会通过connect正常触发组件更新。
额外优化建议
- 避免使用
ReactDOM.findDOMNode获取DOM元素,在Sidebar组件中可以用React的createRef替代:// 在Sidebar的constructor中初始化ref this.addInputRef = React.createRef(); // render中的input <input ref={this.addInputRef} onKeyPress={this.createDeck}></input> // createDeck中获取输入值 const name = this.addInputRef.current.value; - 不要在
mapStateToProps中返回整个State,只返回组件需要的部分,减少不必要的重渲染:// VisibleCards的mapStateToProps示例 function mapStateToProps(state, ownProps) { return { decks: state.decks // 只返回组件需要的状态 }; }
内容的提问来源于stack exchange,提问作者Mohamed Baher

