我从API服务器获取数据以动态生成NavBar菜单。
问题是每次浏览页面时菜单都会重新渲染。
无法弄清楚它为什么会发生。我尝试使用react-router
v4的不同示例,但菜单总是重新渲染。
在动态生成菜单时,您使用什么模式来阻止NavBar菜单重新渲染?
以下是基本设置文件:
Main.js文件:
import React from 'react'
import { Route } from 'react-router-dom'
import Home2 from './Home'
import Head from './Head'
import Rules from './Rules'
const Main = () => (
<main>
<Route path='/' component={Head}/>
<Route exact path='/' component={Home}/>
<Route exact path='/rules' component={Rules}/>
</main>
)
export default Main
Head.js文件:
import React, { Component } from 'react'
import Menu from 'semantic-ui-react'
class Head extends Component {
constructor(props) {
super(props);
}
getInitialData() {
//fetch data from server
}
componentWillMount() {
this.getInitialData();
}
render() {
return (
<header>
<nav>
<Menu>
{/* fetched data */}
</nav>
</header>
)
}
}
export default Head
Index.js文件:
import React from 'react'
import { render } from 'react-dom'
import Main from './components/Main'
import { BrowserRouter } from 'react-router-dom'
render((
<BrowserRouter>
<Main />
</BrowserRouter>
), document.getElementById('root'));
使用React Router v3,此代码可以正常工作:
var Routes = (
<Router>
<Route path="/" component={Head}>
<IndexRoute component={Home} />
</Route>
</Router>
);
但是在v4中,我无法嵌套路线。
答案 0 :(得分:2)
即使已经很晚了,我还是想在这里为其他可能为此而苦苦挣扎的人发布答案。
首先,根据@JulesDupont 的回答,您的 <Head />
组件应该在您的路线之外。
const App = () => (
<>
<Head />
<Switch>
<Route exact path='/' component={Component 1}/>
// Add any other routes goes here
</Switch>
</>
)
export default App;
此外,您正在搜索的模式是使用 <Link>
中的 react-router-dom
标签。如果您可以在这里发布您的 Head 组件,那就太好了。您很有可能在 <a href='/#'>
中使用 <Link to='/#'>
标签而不是 <Menu />
标签进行重定向。
<a>
标签会触发整个页面重新加载,导致每次导航到新页面时重新安装 <Head />
组件,从而重新获取所有数据。但是,<Link>
标记不会触发整页重新加载。
示例:
import { Link } from 'react-router-dom';
const Head = () => {
return (
<ul>
<Link to='/your-route'>Item 1 (fetches data)<Link>
<Link to='/your-other-route'>Item 2 (fetches data)</Link>
</ul>
)
}
export default Head;
这将确保您的 Head 组件在导航到另一条路线时不会重新渲染或重新获取数据。
答案 1 :(得分:1)
因为您将标题包含为路线,所以每次路线更改时都会重新渲染。只需将标题拉出路线,它就会在基于路径的组件发生变化时保持一致:
import React from 'react'
import { Route, Switch } from 'react-router-dom'
import Home2 from './Home'
import Head from './Head'
const Main = () => (
<main>
<Head />
<Switch>
<Route exact path='/' component={Home}/>
// Add any other routes you want here
</Switch>
</main>
)
export default Main;
答案 2 :(得分:0)
我有一个类似的问题,最终我无法解决它,所以请尝试以下方法。 在我的主要路线更新中,如果没有创建它,则请求菜单。
#Route.js
state = {
menu: null
};
componentDidUpdate() {
if ( !this.state.menu ) {
this._instanceMenu();
}
}
render() {
return (
<React.Fragment>
{ this._renderStaticContent() }
<Switch>
<Route exact path={ '/' } component={ DefaultPage } />
<Route component={ Error404 } />
</Switch>
</React.Fragment>
);
};
我第一次通过提取请求它,然后将其存储在localstorage中,之后,如果它已经在localStorage中,则不返回进行提取。
#Route.js
_instanceMenu() {
if ( !this.helper.getMenu() ) {
this.helper.instanceMenu( ( menu ) => {
this.setState( { menu } );
} );
}
this.setState( { menu: this.helper.getMenu() } );
}
可选
#Helper.js
instanceMenu = ( callback ) => {
axios.get(
this._urlMenu,
).then( ( response ) => {
localStorage.setItem( this._menu, JSON.stringify( response.data ) );
callback( this.getMenu() );
} ).catch( ( error ) => {
console.log( error );
} );
};
getMenu = () => {
return JSON.parse( localStorage.getItem( this._menu ) );
};
最后,我将其与Higer-Order组件(Readme)一起发送到我的组件中
#Route.js
_renderStaticContent = () => {
if ( !this.authService.isLoggedIn() ) return null;
return <React.Fragment>
<Route component={ HeaderContainer } />
<Route component={ wrap( SidebarContainer, { 'menu': this.state.menu } ) } />
<Route component={ Footer } />
</React.Fragment>;
};
PD:对不起,我的英语。
答案 3 :(得分:0)
我也遇到了同样的问题,但这与在路由中包含标题无关。实际上,这种情况不太明显,很难确定。
我们从一个自定义钩子中获取一些信息,但是这个自定义钩子是从状态中返回整个对象,而不是破坏特定字段。因此,对象内部的其他字段(我们不感兴趣)正在根据路线更改进行更新,因此这会触发报头的不必要的重新渲染。
仅提供示例;
我们现在在做什么(不正确)
const { auth } = useSelector(state => state);
我们将其更改为
const { user } = useSelector(state => state.auth);
auth中的其他字段正在更改,但我们对此不感兴趣。通过仅破坏用户的结构,其他字段将被忽略,并且重新渲染停止。
我意识到这可能是一个非常独特的场景,但希望它可以对某人有所帮助。