Render a function inside a component in react js - javascript

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>
);
}

Related

You cannot render a <Router> inside another <Router> -- using React Router 6 with Storybook

I am understanding the error message, but not sure how I should handle it in this case. I believe that using the decorator below is causing the issue, but the decorator is needed to use the component with Storybook.
Here is the error message:
You cannot render a <Router> inside another <Router>. You should never have more than one in your app.
Believe this is due to the decorator and I can only assume the BrowserRouter found way upstream in my app, but from what I understand, Storybook isn't loading my index file. So I'm unsure how to proceed.
Here is the component, simplified:
export const Component = () => {
...
return (
<Routes>
<Route path="/screening" element={<Screening {...propBag} />} />
</Routes>
);
};
Then, the Story:
import { Story, Meta } from '#storybook/react';
import { MemoryRouter } from 'react-router-dom';
import { Component } from '..';
export default {
title: 'Province',
component: Component,
decorators: [
(Story) => (
<MemoryRouter>
<Story />
</MemoryRouter>
)
],
} as Meta;
const Template: Story = (args) => <IntakeQuestionnaire {...args} />;
export const Province = Template.bind({});
Province.parameters = {};
Province.args = {};
Finally, the preview.js file:
import 'tailwindcss/tailwind.css';
import { MockedProvider } from '#apollo/client/testing';
import { i18n } from './i18next';
export const parameters = {
i18n,
locale: 'en',
locales: {
en: 'English',
fr: 'Français',
},
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
apolloClient: { MockedProvider },
};
export const decorators = [
(Story) => (
<MemoryRouter>
<Story />
</MemoryRouter>
)
];
Not sure why, but removing the decorators array from preview.js file and putting it only in the component Story file fixed this issue. Less than ideal but at least I am unblocked now
export default {
title: 'Province',
component: Component,
decorators: [
(Story) => (
<MemoryRouter>
<Story />
</MemoryRouter>
)
],
} as Meta;
EDIT: see below comment - i was being silly with decorators

Easier way to dynamically render components with sidebar navigation using react router

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.

React Router Authentication Async

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.

How to get routing working in React.js

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()
})
})

React Router - new component not being loaded

So it's my first time setting something like this up and I'm struggling a little bit.
I used https://github.com/reactjs/react-router/tree/master/examples/huge-apps as my source of learning and am trying to set up a very basic layout for a react application.
What seems to be happening is that react isn't re-rendering after a path change therefore nothing ever gets added to the dom
When I click on the go to home component Link the URL bar changes but no DOM changes occur...
Here is my code [i'm leaving out my directory structure since i don't think it's important for the problem]
index.jsx: Load up the react app and get all routes
import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import { Router, browserHistory } from 'react-router';
import Routes from './app/Routes.js';
render(
<Router
history={browserHistory}
routes={Routes}
/>,
document.querySelector('.js-mount-point')
);
Routes.js: Constant to keep all of my routes so that I don't have to manually specify them in index.js
import App from './App.jsx';
import Home from '../routes/Home/Home.js';
const Routes = {
path: '/',
component: App,
childRoutes: [
Home,
],
};
export default Routes;
App.jsx: Parent most component for my app
import React from 'react';
import { Link } from 'react-router';
const App = props => {
console.log(props);
return (
<div>
<h1>Hello World!</h1>
<p><Link to="#/home">Go to Home Component</Link></p>
{props.children}
</div>
);
};
export default App;
Home.js Grab all my route information and getComponent lives here
const HomeRoute = {
path: 'home',
title: 'Home',
getComponent(nextState, cb) {
require.ensure([], (require) => {
cb(null, require('./HomeComponent.jsx').default);
});
},
};
export default HomeRoute;
HomeComponent.jsx my very basic home component
import React from 'react';
const HomeComponent = () => (
<div>
<h2>Welcome Home</h2>
</div>
);
export default HomeComponent;
Edit1: Made App.jsx pure function
Edit2: Fixed Routes.js
Edit3: Fixed Home.js
Edit4: Final fix,
const HomeComponent =
changed to
const HomeComponent = () => (
You App should be a component not a function returning an object. Right now you are mixing two approaches.
Either a function (stateless component)
import React from 'react';
import { Link } from 'react-router';
const App = props => {
console.log(props.children);
return (
<div>
<h1>Hello World!</h1>
<p><Link to="/home">Go to Home Component</Link></p>
{props.children}
</div>
);
};
export default App;
Or a statefull component that has render method
import React, { Component } from 'react';
import { Link } from 'react-router';
class App extends Component {
render() {
console.log(this.props.children);
return (
<div>
<h1>Hello World!</h1>
<p><Link to="/home">Go to Home Component</Link></p>
{this.props.children}
</div>
);
}
};
export default App;
Same with HomeComponent
const HomeComponent = () => (
<div>
<h2>Welcome Home</h2>
</div>
);
And you need to fix route config as well
const HomeRoute = {
path: 'home', //no # needed
title: 'Home',
getComponent(nextState, cb) {
require.ensure([], (require) => {
cb(null, require('./HomeComponent.jsx').default);
});
},
};
Ohh, and I think you need.
const Routes = {
path: '/',
component: App,
childRoutes: [
Home
],
};

Categories