Handling a universal breadcrumb in a flux application with react-breadcrumbs
But what if your application is not a SPA and part of your routing (and your breadcrumb) happens server-side? This is what we will achieve today with some dirty DOM magic.
First, let's consider our HTML page:
<!DOCTYPE html>
<html>
<head>
<!-- ... -->
</head>
<body>
<div class="breadcrumb"><!-- server-side breadcrumb --></div>
<div id="react"><!-- React app will be rendered here --></div>
<!-- scripts, etc. -->
</body>
And here is our root component:
'use strict';
let React = require('react'),
Breadcrumb = require('react-breadcrumbs');
module.exports = React.createClass({
/**
* @return {Object}
*/
render: function() {
return (
<div className="container">
<Breadcrumb routes={this.props.routes} />
{this.props.children}
</div>
);
}
});
The
Breadcrumb
JSX tag will be rendered as our client-side breadcrumb; what we rather want is to have both breadcrumbs concatenated in our div.breadcrumb
.Let's make some additions to the root component:
'use strict';
let React = require('react'),
Breadcrumb = require('react-breadcrumbs');
module.exports = React.createClass({
/**
* Maintains breadcrumb up-to-date with dynamic, react-router-powered part
*
* The actual breadcrumb is outside React's scope, so we basically render
* a hidden breadcrumb with the aforementioned part and copy its contents
* at the end of it after wiping it clean, all through DOM manipulation.
*/
updateBreadcrumb: function() {
let breadcrumb = document.querySelector('.breadcrumb');
// We use React's own specific HTML attributes to clear the breadcrumb from previous additions
Array.prototype.slice.call(document.querySelectorAll('.breadcrumb > [data-reactid]')).forEach(function(toRemove) {
breadcrumb.removeChild(toRemove);
}, 0);
breadcrumb.innerHTML += document.querySelector('.breadcrumbHolder').innerHTML;
},
componentDidUpdate: function() {
// Trigger breadcrumb update everytime our route changes
this.updateBreadcrumb();
},
/**
* @return {Object}
*/
render: function() {
return (
<div className="container">
{/* Hide client-side breadcrumb and prevent it from explicitly displaying missing segments, if any */}
<Breadcrumb routes={this.props.routes} customClass="breadcrumbHolder hide" displayMissing={false} />
{this.props.children}
</div>
);
}
});
And it works! This is a total hack, but React's very own philosophy prevents us from more interaction with a preexistant DOM, at least for now. Stay tuned!