PrimeReact menubar with React Router
4youngpadawans.com presents PrimeReact menubar with React Router featuring React | PrimeReact
App's navigation system is very important part of GUI.
Clear and easy approachable navigation system significantly improves user experience. On the other side, developers should always aim to create maintainable navigation system so that new navigation routes can be easily added.
In this story we will create navigation system using PrimeReact Menubar with drop-down menus and React Router.
Basics
Before going to next steps, lets suppose that you've already
- created React app,
- added PrimeReact components
- added React Router package to the project
In order to achieve our goal, we will need to do following
- to create several React components that will serve as content containers for app's pages
- to design navigational component in a form of menubar positioned in the page header,
- to provide mechanism allowing user to click on menu item and navigate to related page
As a code base we will use Redux enriched project created in article PrimeReact growl with redux but you can omit Redux related stuff pretty easily.
Accessing session's history
There are several ways to make session's history available to React component. The most convenient one is to utilize withRouter
and to wrap up component with it prior to exporting. On that way history
becomes accessible as one of component's props
.
appmenu.jsx
import React, { Component } from 'react';
import {Menubar} from 'primereact/components/menubar/Menubar';
import {connect} from 'react-redux';
import { withRouter } from 'react-router'
import { showGrowl } from '../actions/actions.jsx'
import { showInfoMessage } from '../utils/utils.js';
export class AppMenu extends Component {
navigateToPage = (path) => {
console.log('Navigate to path ' + path);
this.props.history.push(path);
showInfoMessage(this,'Navigation',path);
}
render() {
var items=[
{label: 'Router navigation', icon: 'fa-compass',
items: [{label: 'Component 1 route',command:()=>{ this.navigateToPage('/component1')}},
{label: 'Component 2 route',command:()=>{ this.navigateToPage('/component2')}},
{label: 'Component 3 route with path param 1',command:()=>{ this.navigateToPage('/component3/1')}},
{label: 'Component 3 route with path param 2',command:()=>{ this.navigateToPage('/component3/2')}},
{label: 'Component 4 route with query string',command:()=>{ this.navigateToPage('/component4?name=sith-lord')}}]}
];
return (<Menubar model={items}/>);
}
}
const mapDispatchToProps = {
showGrowl
};
AppMenu = withRouter(connect(null,mapDispatchToProps)(AppMenu))
export default AppMenu
For more details about how to create Menubar menu items check out all available properties of MenuModel.
Defining navigation routes
We need to make sure that React Router is able to detect programmatic changes of session's history.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux'
import appReducers from './reducers/reducers.js'
import { Router, Route } from 'react-router'
import createBrowserHistory from 'history/createBrowserHistory';
import App from './App';
import './index.css';
import { Component1 } from './components/component1.jsx';
import { Component2 } from './components/component2.jsx';
import { Component3 } from './components/component3.jsx';
import { Component4 } from './components/component4.jsx';
let History = createBrowserHistory();
let store = createStore(appReducers);
const render = () =>
ReactDOM.render(
<Provider store={store}>
<Router history={History}>
<App>
<Route path="/component1" component={Component1} />
<Route path="/component2" component={Component2} />
<Route path="/component3/:id" component={Component3} />
<Route path="/component4" component={Component4} />
</App>
</Router>
</Provider>,
document.getElementById('root')
);
render();
We actually did following:
- we took session's history by the neck using history library
- then we passed it to
history
property of<Router>
component.
On that way, Router
will be aware of session's history object changes and when any new URL is pushed in history with this.props.history.push(path)
, React Router will take care that related component/page is mounted and rendered.
Notice that
component3
andcomponent4
are defined to receive additional parameters from URL's path and query string respectively.
React components as pages
Let's create components that will serve as containers for pages as well as for functionalities inside each page.
component1.jsx
import React, { Component } from 'react';
export class Component1 extends Component {
render(){
return <div><h1>I am component 1</h1></div>;
}
}
component2.jsx
Just another simple React component...
import React, { Component } from 'react';
export class Component2 extends Component {
render(){
return <div><h1>I am component 2</h1></div>;
}
}
component3.jsx
This component will receive URL path parameter as id
property of React Router's match.params
object.
import React, { Component } from 'react';
export class Component3 extends Component {
constructor(props) {
super(props);
this.state = { params: null };
}
static getDerivedStateFromProps(nextProps, prevState) {
return { params: nextProps.match.params };
}
render() {
return <div><h1>I am component 3</h1> <h2>with path param {this.state.params.id}</h2></div>;
}
}
component4.jsx
This component will receive query string parameter as search
property of React Router's location
object.
import React, { Component } from 'react';
import queryString from 'query-string';
export class Component4 extends Component {
constructor(props) {
super(props);
this.state = { search: null };
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.location.search !== prevState.search) {
return { search: nextProps.location.search };
}
return null;
}
render() {
//following statement parses query string into json object
const query = queryString.parse(this.state.search);
return <div><h1>I am component 4</h1> <h2>with query param 'name'={query.name}</h2></div>;
}
}
Since React 16.3 some life-cycle methods of React component have been deprecated. So instead of deprecated
componentWillReceiveProps
we are using newly introduced static methodgetDerivedStateFromProps
.