I'm working on the following react project, and am trying to get the relative links in the navbar to work properly. However, when I click on About Me I get the following response,
Cannot GET /about
The code I am working with looks like the following,
App.js
// This component handles the App template used on every page.
import React, {PropTypes} from 'react';
import Header from './common/Header';
import NavBar from './common/navbar';
import Router from 'react-router';
import { Link, IndexLink } from 'react-router';
var navbar = {};
navbar.brand = {linkTo: "http://chrisrjones.com", text: "chrisrjones.com"};
navbar.links = [
// <Link to="/about" activeClassName="active">About</Link>
{linkTo: "/about", text: "About Me"},
{linkTo: "#", text: "Contact"},
{dropdown: true, text: "Contribute", links: [
{linkTo: "#", text: "Sign Up"},
{linkTo: "#", text: "Login"}
]}
];
class App extends React.Component {
render() {
return (
<div className="container-fluid">
<NavBar {...navbar}/>
<Header/>
{this.props.children}
</div>
);
}
}
App.propTypes = {
children: PropTypes.object.isRequired
};
export default App;
Also, to get an idea of what the navbar code looks like you can check out this codepen that I referenced for the navbar code in my project.
Looking inside your Navbar component I found one things that is may be causing this issue, there are two approach for that:
Using href, you should use absolute path, not relative
Use Link from react-router instead of 'a':
Example approach 1:
<a className="navbar-brand" href={ 'http://www.absolutepath.com' + this.props.linkTo}>{this.props.text}</a>
Example approach 2:
import { Link } from 'react-router'
<Link to={ this.props.linkTo }>
<span className="navbar-brand">{this.props.text}</span>
</Link>
In your server.js, redirect all routes requests to the root /index.html file (ignoring file requests):
app.use('*', function (req, res, next) {
const filename = path.join(compiler.outputPath, 'index.html')
compiler.outputFileSystem.readFile(filename, (err, result) => {
if (err) {
return next(err)
}
res.set('content-type', 'text/html')
res.send(result)
res.end()
})
})
Related
I have a website made with Docusaurus v2 that currently contains documentation. However, I would like to add a page of a list of workflows where if a workflow in the list is clicked, the user would be shown a page of additional details of that workflow. For now it seems docusaurus.config seems to be handling most of the routing, but is there a way I can add a dynamic route like /workflows/:id? I made a separate standalone app which had a Router object and it worked if my App.js looks like this:
// App.js
import Navigation from './Navigation'
import {BrowserRouter as Router, Switch, Route} from 'react-router-dom';
function App() {
return (
<Router>
<Navigation />
<Switch>
<Route path="/" exact component={Home}></Route>
<Route path="/workflows" exact component={Workflows}></Route>
<Route path="/workflows/:id" component={WorkflowItem}></Route>
</Switch>
</Router>
)
}
Is it possible to add the Router somewhere in Docusaurus?
Thanks!
I solved this by creating a simple plugin to add my own custom routes. Documentation here.
Let's call the plugin plugin-dynamic-routes.
// {SITE_ROOT_DIR}/plugin-dynamic-routes/index.js
module.exports = function (context, options) {
return {
name: 'plugin-dynamic-routes',
async contentLoaded({ content, actions }) {
const { routes } = options
const { addRoute } = actions
routes.map(route => addRoute(route))
}
}
}
// docusaurus.config.js
const path = require('path')
module.exports = {
// ...
plugins: [
[
path.resolve(__dirname, 'plugin-dynamic-routes'),
{ // this is the options object passed to the plugin
routes: [
{ // using Route schema from react-router
path: '/workflows',
exact: false, // this is needed for sub-routes to match!
component: '#site/path/to/component/App'
}
]
}
],
],
}
You may be able to use the above method to configure sub-routes as well but I haven't tried it. For the custom page, all you need is the Switch component (you are technically using nested routes at this point). The Layout component is there to integrate the page into the rest of the Docusaurus site.
// App.js
import React from 'react'
import Layout from '#theme/Layout'
import { Switch, Route, useRouteMatch } from '#docusaurus/router'
function App() {
let match = useRouteMatch()
return (
<Layout title="Page Title">
<Switch>
<Route path={`${match.path}/:id`} component={WorkflowItem} />
<Route path={match.path} component={Workflows} />
</Switch>
</Layout>
)
}
My target is to create a breadcrumb component in react js.
I used Ant Design in my App, but I have some issues with a parth of it.
In the documentation of Ant Design i found a solution for creating dynamic breadcrumbs, but can't find out how to apply the code.
Using, Ant design i built the next app:
import React from "react";
import {Link, BrowserRouter, Route} from "react-router-dom";
import {Breadcrumb} from 'antd';
//from here starts the code from Ant Design Documentation
const routes = [
{
path: 'index',
breadcrumbName: 'home',
},
{
path: 'first',
breadcrumbName: 'first',
children: [
{
path: '/general',
breadcrumbName: 'General',
},
{
path: '/layout',
breadcrumbName: 'Layout',
},
{
path: '/navigation',
breadcrumbName: 'Navigation',
},
],
},
{
path: 'second',
breadcrumbName: 'second',
},
];
function itemRender(route, params, routes, paths) {
const last = routes.indexOf(route) === routes.length - 1;
return last ? (
<span>{route.breadcrumbName}</span>
) : (
<Link to={paths.join('/')}>{route.breadcrumbName}</Link>
);
}
return <Breadcrumb itemRender={itemRender} routes={routes} />;
//here is the end of the code from Ant Design
function App() {
return (
<div>
<p>here i want to render my breadcrumb</p>
</div>
);
}
export default App;
Also, the return statement is located outside of the function and i get error due of this.
How to create, using this implementation, a breadcrumb, and how to render itemRender function inside my component, and from where should i get these params itemRender(route, params, routes, paths)?
You need to use Breadcrum component in App body
function App() {
return (
<div>
<Breadcrumb itemRender={itemRender} routes={routes} />
</div>
);
}
Currently going through this tutorial on creating a sidebar navigation system with react router https://reacttraining.com/react-router/web/example/sidebar
I am planning to have multiple routes, so that means I'll have to keep importing the routes and add them to the routes array. Is there a smart/right way to load them dynamically?
All my components will be in my /Views folder.
App.js
import React, { Component } from 'react';
import SideBar from './components/SideBar/SideBar';
import MainContent from './components/MainContent/MainContent';
import { BrowserRouter as Router,
} from 'react-router-dom';
// Import all components here
import Button from './components/Views/Button/Button';
import Color from './components/Views/Color/Color';
import Card from './components/Views/Card/Card';
import Filter from './components/Views/Filter/Filter';
const routes = [
{
path: "/",
name: 'home',
exact: true,
main: () => <h2>Home</h2>
},
{
path: "/button",
name: 'Button',
main: () => <Button />
},
{
path: "/color",
name: 'Color',
main: () => <Color />
},
{
path: "/card",
name: 'Card',
main: () => <Card />
},
{
path: "/filter",
name: 'Filter',
main: () => <Filter />
},
];
class App extends Component {
render() {
return (
<Router>
<div className="ds-container">
<SideBar routes={routes} />
<MainContent routes={routes} />
</div>
</Router>
);
}
}
export default App;
Since, you're using create-react-app that uses webpack internally, you could look into require-context. This will help you dynamically import all files in a folder that match a certain regex. (ex: ending with .jsx/.js)
However, I'd advice you against it as:
At no point will you know what routes you're currently catering to.
It may decrease your code readability.
You may have to also export the mapping(path in the Route) of the component along with the component itself.
To avoid all of this, You could simply create a index.js file in your Views component that would require any new route component that you create and return the final array that you have formed in the App.js file.
So essentially, /Views/index.js :
// Import all components here
import Button from './components/Views/Button/Button';
import Color from './components/Views/Color/Color';
import Card from './components/Views/Card/Card';
import Filter from './components/Views/Filter/Filter';
const routes = [
{
path: "/",
name: 'home',
exact: true,
main: () => <h2>Home</h2>
},
{
path: "/button",
name: 'Button',
main: () => <Button />
},
{
path: "/color",
name: 'Color',
main: () => <Color />
},
{
path: "/card",
name: 'Card',
main: () => <Card />
},
{
path: "/filter",
name: 'Filter',
main: () => <Filter />
},
// add new routes here
];
export default routes;
In SideBar.js:
import routes from 'path/to/views/Views';
//rest of your code to render the routes.
This way, you would clean up the code in your App.js and would also be able to effectively separate the concerns of the individual components.
I hope this makes sense :)
There are several ways to choose a main component depending on current location. But all of them would require listing all the possible routes and importing respective components. You can use dynamic imports and React.lazy for easier code splitting.
But you can avoid extra configuration for your sidebar. The sidebar can get configuration from global state and your main components can update that state on mount (componentDidMount or useEffect(() => {...}, [])):
const SideBarContext = React.createContext(() => {});
function useSidebar(name) {
const setSidebarName = useContext(SideBarContext);
useEffect(() => {
setSidebarName(name);
return () => setSidebarName(null);
}, []);
}
function Home() {
useSidebar('home');
return <h2>Home</h2>;
}
function Button() {
useSidebar('Button');
return <button>press me</button>;
}
function App() {
const [name, setName] = useState(null);
return (
<Router>
<div className="ds-container">
<SideBar name={name} />
<SideBarContext.Provider value={setName}>
<Route path="/" exact component={Home}/>
<Route path="/button" component={Button}/>
</SideBarContext.Provider>
</div>
</Router>
);
}
So each component will take care about options for sidebar and you only need to import them and add Routes.
I'm new to react.
I want to add some security to my Async-Routes which I implemented in my routes.js:
import React from 'react';
import App from './app.jsx';
import Home from './components/home.jsx';
import {Router, Route, IndexRoute, hashHistory} from 'react-router';
function loadRoute(cb) {
return (module) => cb(null, module.default);
}
const routes = {
component: App,
childRoutes: [
{
path: "/",
component: Home
},
{
path: "/hello/:foo",
getComponent(location, cb) {
System.import('./components/hello.jsx')
.then(loadRoute(cb))
.catch(errorLoading);
}
},
]
};
export default () => <Router history={hashHistory} routes={routes} />
As you can see the "/hello/:foo" route is async.
How can I restrict the access to this route (role-based) and redirect to somewhere else (e.g. login)?
I want to load the chunk only when it's needed.
Should I place the checking code into "getComponent()"?
Can it be done with "willTransitionTo()", will this function be executed before "getComponent()" and how should I implement it?
I would place the checking code into componentWillMount(), and in render() return the page component, or display/redirect to login.
If you have multiple page that need access restricted, I'd create a high level component order for each page component to check access before rendering.
Everything is about isomorphic application. I'm using React with react-router module on server side for routing purposes and have following warning in browser console.
Warning: render(...): Replacing React-rendered children with a new
root component. If you intended to update the children of this node,
you should instead have the existing children update their state and
render the new components instead of calling ReactDOM.render.
I have following routes schema defined on backend:
<Route path="/" component={App} >
<IndexRoute component={Home} />
</Route>
App component:
module.exports = React.createClass({
render : function() {
return <html>
<head></head>
<body>
<div id="container">
{ this.props.children }
</div>
<script src="/app/bundle.js"></script>
</body>
</html>
}
});
Home component:
module.exports = React.createClass({
render : function() {
return <div>Any content here</div>
}
});
After that I use on the frontend:
ReactDOM.render(<Home />, document.getElementById('container'));
Probable solution:
If I understood correctly if I could render App component as static markup(renderToStaticMarkup) and Home component as a string (renderToString), then it would be ok.
Is it possible to implement something like that with react-router?
Assuming RR 1.03, your routing configuration looks fine.
Your app component should be like this:
module.exports = React.createClass({
render : function() {
return <html>
<head></head>
<body>
<div id="container">
{React.cloneElement(this.props.children,
{
anyOtherPropsHere: 'blablah'
}
)}
</div>
<script src="/app/bundle.js"></script>
</body>
</html>
}
});
Your server response -> render should look something like this. (taken from the documentation)
import { renderToString } from 'react-dom/server'
import { match, RouterContext } from 'react-router'
import routes from './routes'
serve((req, res) => {
// Note that req.url here should be the full URL path from
// the original request, including the query string.
match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
if (error) {
res.status(500).send(error.message)
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search)
} else if (renderProps) {
// You can also check renderProps.components or renderProps.routes for
// your "not found" component or route respectively, and send a 404 as
// below, if you're using a catch-all route.
res.status(200).send(renderToString(<RouterContext {...renderProps} />))
} else {
res.status(404).send('Not found')
}
})
})
And finally, somewhere on clientside load, do something like this. I've added the history library in this example to help.
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, match, RoutingContext } from 'react-router';
import history from 'utils/history';
import AppRoutes from '/app/AppRoutes';
// use this function to return a Route component with the right props
function createFluxComponent(Component, props) {
props = Object.assign(props, {
flux: window.flux.or.whatevs
});
return <Component {...props} />;
}
// add a global history listener perhaps?
history.listen(function(location) {
console.log('Transition to--------------', location.pathname);
});
// add window.router polyfill for transitionTo
window.router = {
transitionTo: function(t) {
return history.pushState(null, t);
}
};
// render your routing configuration, history and flux as props
ReactDOM.render(<Router createElement={createFluxComponent} history={history} routes={AppRoutes}/>, document);
What is most important is that you render to string using the RouterContext and render props on the server side. You get the renderProps from the RR match function, which will run your routes against the config and produce the right component in renderProps. Then you'll simply render the client side with Router element and the router config to the document. Does this make sense? It should work without any invariants.