Giter Site home page Giter Site logo

icd2k3 / react-router-breadcrumbs-hoc Goto Github PK

View Code? Open in Web Editor NEW
301.0 7.0 42.0 2.73 MB

tiny, flexible, HOC for rendering route breadcrumbs with react-router v4 & 5

Home Page: https://codesandbox.io/s/react-router-breadcrumbs-hoc-demo-iqmx6?file=/Breadcrumbs.js

License: MIT License

JavaScript 62.31% Shell 3.34% TypeScript 34.35%
breadcrumbs react-router hoc

react-router-breadcrumbs-hoc's Introduction

LEGACY

This repository is for legacy support of react-router v5.

Please use use-react-router-breadcrumbs and react-router v6 instead.


React Router Breadcrumbs HOC

A small (~1.3kb compressed & gzipped), flexible, higher order component for rendering breadcrumbs with react-router 5


example.com/user/123 → Home / User / John Doe


Description

Render breadcrumbs for react-router however you want!

Features

  • Easy to get started with automatically generated breadcrumbs.
  • Render, map, and wrap breadcrumbs any way you want.
  • Compatible with existing route configs.

Install

yarn add react-router-breadcrumbs-hoc

or

npm i react-router-breadcrumbs-hoc --save

Usage

withBreadcrumbs()(MyComponent);

Examples

Simple

Start seeing generated breadcrumbs right away with this simple example (codesandbox)

import withBreadcrumbs from 'react-router-breadcrumbs-hoc';

const Breadcrumbs = ({ breadcrumbs }) => (
  <>
    {breadcrumbs.map(({ breadcrumb }) => breadcrumb)}
  </>
)

export default withBreadcrumbs()(Breadcrumbs);

Advanced

The example above will work for some routes, but you may want other routes to be dynamic (such as a user name breadcrumb). Let's modify it to handle custom-set breadcrumbs. (codesandbox)

import withBreadcrumbs from 'react-router-breadcrumbs-hoc';

const userNamesById = { '1': 'John' }

const DynamicUserBreadcrumb = ({ match }) => (
  <span>{userNamesById[match.params.userId]}</span>
);

// define custom breadcrumbs for certain routes.
// breadcumbs can be components or strings.
const routes = [
  { path: '/users/:userId', breadcrumb: DynamicUserBreadcrumb },
  { path: '/example', breadcrumb: 'Custom Example' },
];

// map, render, and wrap your breadcrumb components however you want.
const Breadcrumbs = ({ breadcrumbs }) => (
  <div>
    {breadcrumbs.map(({
      match,
      breadcrumb
    }) => (
      <span key={match.url}>
        <NavLink to={match.url}>{breadcrumb}</NavLink>
      </span>
    ))}
  </div>
);

export default withBreadcrumbs(routes)(Breadcrumbs);

For the above example...

Pathname Result
/users Home / Users
/users/1 Home / Users / John
/example Home / Custom Example

Route config compatibility

Add breadcrumbs to your existing route config. This is a great way to keep all routing config paths in a single place! If a path ever changes, you'll only have to change it in your main route config rather than maintaining a separate config for react-router-breadcrumbs-hoc.

For example...

const routeConfig = [
  {
    path: "/sandwiches",
    component: Sandwiches
  }
];

becomes...

const routeConfig = [
  {
    path: "/sandwiches",
    component: Sandwiches,
    breadcrumb: 'I love sandwiches'
  }
];

then you can just pass the whole route config right into the hook:

withBreadcrumbs(routeConfig)(MyComponent);

Dynamic breadcrumbs

If you pass a component as the breadcrumb prop it will be injected with react-router's match and location objects as props. These objects contain ids, hashes, queries, etc from the route that will allow you to map back to whatever you want to display in the breadcrumb.

Let's use Redux as an example with the match object:

// UserBreadcrumb.jsx
const PureUserBreadcrumb = ({ firstName }) => <span>{firstName}</span>;

// find the user in the store with the `id` from the route
const mapStateToProps = (state, props) => ({
  firstName: state.userReducer.usersById[props.match.params.id].firstName,
});

export default connect(mapStateToProps)(PureUserBreadcrumb);

// routes = [{ path: '/users/:id', breadcrumb: UserBreadcrumb }]
// example.com/users/123 --> Home / Users / John

Now we can pass this custom redux breadcrumb into the HOC:

withBreadcrumbs([{
  path: '/users/:id',
  breadcrumb: UserBreadcrumb
}]);

Similarly, the location object could be useful for displaying dynamic breadcrumbs based on the route's state:

// dynamically update EditorBreadcrumb based on state info
const EditorBreadcrumb = ({ location: { state: { isNew } } }) => (
  <span>{isNew ? 'Add New' : 'Update'}</span>
);

// routes = [{ path: '/editor', breadcrumb: EditorBreadcrumb }]

// upon navigation, breadcrumb will display: Update
<Link to={{ pathname: '/editor' }}>Edit</Link>

// upon navigation, breadcrumb will display: Add New
<Link to={{ pathname: '/editor', state: { isNew: true } }}>Add</Link>

Options

An options object can be passed as the 2nd argument to the hook.

withBreadcrumbs(routes, options)(Component);
Option Type Description
disableDefaults Boolean Disables all default generated breadcrumbs.
excludePaths Array<String> Disables default generated breadcrumbs for specific paths.

Disabling default generated breadcrumbs

This package will attempt to create breadcrumbs for you based on the route section. For example /users will automatically create the breadcrumb "Users". There are two ways to disable default breadcrumbs for a path:

Option 1: Disable all default breadcrumb generation by passing disableDefaults: true in the options object

withBreadcrumbs(routes, { disableDefaults: true })

Option 2: Disable individual default breadcrumbs by passing breadcrumb: null in route config:

{ path: '/a/b', breadcrumb: null }

Option 3: Disable individual default breadcrumbs by passing an excludePaths array in the options object

withBreadcrumbs(routes, { excludePaths: ['/', '/no-breadcrumb/for-this-route'] })

Order matters!

Consider the following route configs:

[
  { path: '/users/:id', breadcrumb: 'id-breadcrumb' },
  { path: '/users/create', breadcrumb: 'create-breadcrumb' },
]

// example.com/users/create = 'id-breadcrumb' (because path: '/users/:id' will match first)
// example.com/users/123 = 'id-breadcumb'

To fix the issue above, just adjust the order of your routes:

[
  { path: '/users/create', breadcrumb: 'create-breadcrumb' },
  { path: '/users/:id', breadcrumb: 'id-breadcrumb' },
]

// example.com/users/create = 'create-breadcrumb' (because path: '/users/create' will match first)
// example.com/users/123 = 'id-breadcrumb'

API

Route = {
  path: String
  breadcrumb?: String|Component // if not provided, a default breadcrumb will be returned
  matchOptions?: {             // see: https://reacttraining.com/react-router/web/api/matchPath
    exact?: Boolean,
    strict?: Boolean,
  }
}

Options = {
  excludePaths?: string[]       // disable default breadcrumb generation for specific paths
  disableDefaults?: Boolean  // disable all default breadcrumb generation
}

// if routes are not passed, default breadcrumbs will be returned
withBreadcrumbs(routes?: Route[], options?: Options): HigherOrderComponent

// you shouldn't ever really have to use `getBreadcrumbs`, but it's
// exported for convenience if you don't want to use the HOC
getBreadcrumbs({
  routes: Route[],
  options: Options,
}): Breadcrumb[]

react-router-breadcrumbs-hoc's People

Contributors

9renpoto avatar abhinavsau avatar dependabot[bot] avatar icd2k3 avatar mrchief avatar taschetto avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-router-breadcrumbs-hoc's Issues

Omitting breadcrumb for a path

Thank you for this elegantly simple library!

Quick question: Sometimes routes don't require a breadcrumb.. would it be more flexible to allow for perhaps a null breadcrumb in order to omit it? Right now doing something silly like using noop and checking while rendering, but perhaps checking for a present but null prop instead of throwing an error could be more forgiving?

Please help with this code base.

I am unable to read the breadcrumb props.
What am i missing ?

import BreadCrumb from '../../../../components/bread-crumb';
import {scenarioManagerRoutes} from '../../routes';
class ScenarioManagerHome extends React.PureComponent<Props, State> {
render() {
		const scenarioModelList = this.props.scenarioModels.data;
		const breadcrumbs = this.props; // unable to read props
}
}

export default withBreadcrumbs(scenarioManagerRoutes)(ScenarioManagerHome);

const _ScenarioManagerHome = connect(
	(state) => ({
		isLoading: state.scenarioManager.isLoading,
	}),
	(dispatch) =>
		bindActionCreators(
			{ stopScenarioFlow },
			dispatch,
		),
)(ScenarioManagerHome);

export { _ScenarioManagerHome as ScenarioManagerHome };

Breadcrumbs added data that does not exist

If I have this route:

const routes = [{
    component: Projects,
    path: "/projects",
    breadcrumb: "projects"
  },
  {
    component: Project,
    path: "/projects/:idProject",
    breadcrumb: "project"
  },
  {
    component: Image,
    path: "/projects/:idProject/images/:idImage",
    breadcrumb: "image"
  }
];

The Breadcrumbs will print: Home / projects / 21 / Images / 44 but Images is not defined, because I'm not using Images in my routes.

Dist files use ES6 syntax

I just updated to version 3.0.0 which fixed the warning issue for me (thank you!), but now i get the following error during build:

ERROR in vendors~main.ecb9379d.js from UglifyJs
Invalid assignment [./node_modules/react-router-breadcrumbs-hoc/dist/es/index.js:4277,0][vendors~main.ecb9379d.js:54665,35]

As far as I can tell this is due to the humanize-string dependency which now requires Node.js 6. Should the ES6 syntax from humanize-string be transformed on build for this project?

Example code key in map is undefined

In the sample code

  <div>
    {breadcrumbs.map((breadcrumb, index) => (
      <span key={breadcrumb.props.key}>
        <NavLink to={breadcrumb.props.match.url}>
          {breadcrumb}
        </NavLink>
        {(index < breadcrumbs.length - 1) && <i> / </i>}
      </span>
    ))}
  </div>

<span key={breadcrumb.props.key}> should be replaced with <span key={breadcrumb.key}> as breadcrumb.props does not contain a key in props

Optional Url Params

Hi,

Love the project; thanks,

I have a problem with a path with an optional parameter.

When navigating to /sensor/register the breadcrumb shows:

Register Sensor

but navigating to /sensor/register/123 results in:

Register Sensor / Register Sensor

I have the following path setup in my routes.config - the offending route is at the bottom...

const routesConfig = [
  {
    path: '/hubs',
    exact: true,
    component: MyHubsContainer,
    breadcrumb: 'My Hubs'
  },
  {
    path: '/hubs/register',
    exact: true,
    component: RegisterHubContainer,
    breadcrumb: 'Register Hub'
  },
  {
    path: '/hubs/:hubId',
    exact: true,
    component: HubDashboardContainer,
    breadcrumb: HubBreadcrumbItem
  },
  {
    path: '/hubs/:hubId/sensor/:sensorId/:sensorName',
    exact: true,
    component: SensorDetailContainer,
    breadcrumb: SensorBreadcrumbItem
  },
  {
    path: '/sensor/instructions',
    exact: true,
    component: InstructionsContainer
  },
  {
    path: '/sensor/register/:hubId?',
    exact: true,
    component: RegisterSensorContainer,
    breadcrumb: 'Register Sensor'
  }
]
const MainRoutes = (props) =>
  <Switch>
    {routesConfig.map((route, i) =>
      <Route
        exact={route.exact}
        key={route.path}
        path={route.path}
        component={route.component}
      />)}
  </Switch>
const Breadcrumbs = ({ breadcrumbs }) =>
  <div>
    {breadcrumbs.map((breadcrumb, index) => (
      <Link key={breadcrumb.key} to={breadcrumb.props.match.url || ''}>
        {breadcrumb} {console.log('breadcrumb', breadcrumbs.length, breadcrumb)}
      </Link>
    ))}
  </div>

Any ideas?

Disabling default generated breadcrumbs by passing breadcrumb: null raises an error

According to docs (option 2) https://github.com/icd2k3/react-router-breadcrumbs-hoc#disabling-default-generated-breadcrumbs im able to disable breadcrumb by passing somthing like this:
{ path: '/a/b', breadcrumb: null }. And it's works but typescript type checker raises an error/warning:

Argument of type '({ path: string; breadcrumb: string; } | { path: string; breadcrumb: null; })[]' is not assignable to parameter of type 'BreadcrumbsRoute[]'.
  Type '{ path: string; breadcrumb: string; } | { path: string; breadcrumb: null; }' is not assignable to type 'BreadcrumbsRoute'.
    Type '{ path: string; breadcrumb: null; }' is not assignable to type 'BreadcrumbsRoute'.
      Types of property 'breadcrumb' are incompatible.
        Type 'null' is not assignable to type 'string | ComponentClass<any, any> | FunctionComponent<any> | ComponentClass<{}, any> | FunctionComponent<{}> | undefined'

I can pass { path: '/a/b', breadcrumb: undefined } to silence the error but it will not disable /a/b route. Is there any workaround for this or BreadcrumbsRoute type definition needs some updates?

Property 'match' does not exist on type 'ReactNode'

Version: "react-router-breadcrumbs-hoc": "^3.3.0",

Code:

return (
    <>
      {isBreadCrumbVisible ? (
        <Breadcrumb style={{ marginBottom: '1rem' }}>
          {breadcrumbs.map(({ match, breadcrumb }) => (
            <Breadcrumb.Item key={match.url}>
              <NavLink to={match.url}>{breadcrumb}</NavLink>
            </Breadcrumb.Item>
          ))}
        </Breadcrumb>
      ) : (
          ''
        )}
    </>
  );

TypeScript throws the error for match and: Property 'breadcrumb' does not exist on type 'ReactNode'.

This happened after I updated my package, what am I missing?

typescript definition mistake

export default function withBreadcrumbs

(
routes: BreadcrumbsRoute[],
options?: Options
): withRouter<InjectedProps

>;

should be:

export default function withBreadcrumbs

(
routes?: BreadcrumbsRoute[],
options?: Options
): withRouter<InjectedProps

>;

routes should be optional

Question: Is there a way to not export the current page in breadcrumbs

Hello,

Is there a way to not export the current page breadcrumb?

I.e. we have a page like /test/user (you'd be on the User Page).
The current setup exports:
Home > Test > User

Is it possible to only export Home > Test

I cannot just set the User breadcrumb to null as we'd like the same to occur when on the Test Page. The test page would currently export:
Home > Test

We'd only like 'Home' to show for that page.

Typo

Your sample code says 'withBreadcumbs'... took me some time to spot that it is missing the 'r' ;)

Question: breadcrumbs propTypes

Hello,

Could you share what the {breadcrumbs} propTypes are please? I'm using a strict eslint project which requires PropTypes to be declared.

I can see it is an array, but do you know what the inner object layout for breadcrumbs are?

Thanks

Need help in route config

When i go to route /scenario/edit/:id I need breadscrumb as Home/scenario/editwithid currently getting edit scenario twice.

export const scenarioManagerRoutes = [
	{ path: '/scenario/', component: ScenarioManagerHome, exact: true, breadcrumb: ()=> <React.Fragment><MyIcon class="icon-flow-tree"/>Scenario</React.Fragment>},
	{ path: '/scenario/create', component: ScenarioModelComposer, exact: true, breadcrumb: ()=> <React.Fragment><MyIcon class="icon-file2"/>Create Scenario</React.Fragment> },
	{ path: '/scenario/edit/:id?', component: ScenarioModelComposer, exact: true, breadcrumb: 'Edit Scenario', },
	{ path: '/scenario/details/:id?', component: ScenarioModelDetails, exact: true, breadcrumb: 'Scenario Details', },
];

Also if my route is /admin/apps/list I just want bread crumb as home/admin/list. How do i acheive this ?

Path matching

I've ran into a case when path matching didn't follow react-router - I've got a few routes set up as this:

.../reports
.../reports/:id
.../reports/create

When accessing ...reports/create, router displays the correct page, but the breadcrumb gets a wrong match - breadcrumb returned from HOC matches the .../reports/:id path, with create as id value.

I've patched the problem by changing from .../reports/:id to .../reports/details/:id.

Thanks for the awesome library, this is the first issue I've had with it. I'll be glad to provide more info if necessary.

How to remove "-" from titles

I am getting some items from an API and the titles of the pages are left with the "-" that comes from the slug.
Can custom names be passed to it in "Route" component?

Example:

<Route path="/items/:id" title="Custom name" />

Support for nested routes

Thanks fo this, made my life easier for a breadcrumb navigation for a product I am working on! :-)

We have a routing structure like this:

export default [
  {
    path: '/',
    component: () => <Redirect to='/projects' />,
    exact: true,
    breadcrumb: '',
  },
  {
    path: '/projects',
    component: ProjectListPage,
    breadcrumb: 'My Projects',
    exact: true,
  },
  {
    path: '/projects/:projectId',
    component: ProjectContainer,
    breadcrumb: ({ match }) => match.params.projectId,
    routes: [
      {
        path: '/projects/:projectId/discussions',
        component: DiscussionsPage,
        breadcrumb: 'Discussions',
      },
      {
        path: '/projects/:projectId/applications',
        component: ApplicationsPage,
        breadcrumb: 'Applications',
      },
      {
        path: '/projects/:projectId/collaborators',
        component: CollaboratorsPage,
        breadcrumb: 'Collaborators',
      },
    ],
  },

And the HOC is working perfectly for the first level routes - the thing is that we have a nested routes: [...] which also contains breadcrumb, and they do not appear.

Could the HOC be recursively by adding which prop to traverse for breadcrumbs?

All the best,
Jan

Example how to implement breadcrumbs using router config

I'm currently using router config and trying to figure out how to implement your solution with router config.

In the documentation you're writing:

Already using a route config array with react-router?
Just add a breadcrumb prop to your routes that require custom breadcrumbs.

I honestly don't know how to "Just add a breadcrumb prop ..." with router config. An example how to use those together would be much appreciated.

Unnecessary peer dependency on react-router-dom

Please remove the dependency react-router-dom completely from your project.

The only import in your source code that points to react-router-dom is actually part of react-router.

In your tests you can easily replace the imported NavLink with an "a" tag. I have a PR ready, but you're currently not allowing to provide PRs.

And please don't forget to remove the corresponding peer dependency in your package.json.

Better defaults!

Love your work here @icd2k3! Found it reading through remix-run/react-router#4556.

I think we can do something to improve the defaults and avoid configuration for most cases. Here's how:

Instead of requiring this object every time you're going to use the HOC

const routes = [
  { path: '/', breadcrumb: 'Home' },
  { path: '/users', breadcrumb: 'Users' },
  { path: '/users/:userId', breadcrumb: UserBreadcrumb },
  { path: '/something-else', breadcrumb: ':)' },
];

we could have, by default, a text transformation over the chunks of the URL.

users > Users
something-else > Something Else
something-with-even-more-hyphens > Something with even more hyphens

This would be an incremental upgrade: you can still pass the routes param to the HOC to set custom breadcrumbs on a per-route basis.

To achieve this string-humanization-by-default I suggest using https://github.com/sindresorhus/humanize-string.

Let me know if you think this is a good idea, I'm more than willing to submit a PR if that's the case.

Thanks for your work.

Correctly destructure component in withBreadcrumbs.js

Within the file: withBreadcrumbs.js, I am trying to destructure the follow section:

const render = ({ breadcrumb, match, location, ...rest }) => { const componentProps = { match, location, key: match.url, ...rest }; if (typeof breadcrumb === 'function') { return createElement(breadcrumb, componentProps); } return createElement('span', componentProps, breadcrumb); };

My problem is that this component gets lumped into rest and then outputted into the span where it throws a console warning (due to some other internal workings in my app that don't have anything to do with breadcrumbs). Any insight is appreciated

Issue with excludePaths not being consistent with breadcrumbs: null

I think there is an issue with the excludePaths.

const routesConfig: BreadcrumbsRoute[] = [
  {
    path: '/path2',
    breadcrumb: null,
  },
  {
    path: '/path2/:organization',
    breadcrumb: null
  },
  {
    path: '/path2/:organization/:userId',
    breadcrumb: () => 'organization userId'
  }
];

In the above example, '/path2/:organization' & '/path2' will not render which is what i want and '/path2/:organization/:userId' will render.

I expected to get the same results if i went with excludePaths:

const excludePaths = [
  '/path2/:organization',
  '/path2',
];

Based on the readme, I expected the two to reproduce the same result, but that is not the case.

Thanks,
Derek

Please use yarn to install dependencies, not npm

You suggest to use npm install react-router-breadcrumbs-hoc --save in the README file but the preinstall script in package.json throws an error with the following message: Please use yarn to install dependencies, not npm.

BTW, I'm wondering why there's such a requirement?

Real example

Hi everybody!
Could somebody show a full example of dynamic route with converting userID to "John Doe"?

I can't figure out how to transfer data from axios/redux/anywere to the UserBreadcrumb component...

Thanks for any help

How to map props of Component into breadcrumb title ?

Hi There

this is amazing component I have been used, however I have a question how to get the props into breadcrumb text

e.g: users/:id , I have object in props let say {id:1, name:'john'}
I expect it should be users/john

Thanks

render "home" breadcrumb

Hi,

react-router works really good, thanks for publish your project.

Question: How can I do to display the breadcrumb like this ->

home / user / Jonny

Including NavLink to Home page ?

I' m doing this but not shows the home link.

const routes = [
{ path: '', breadcrumb: 'Home' },
{ path: 'users', breadcrumb: 'Users' },
{ path: 'users/:userId', breadcrumb: UserBreadcrumb},
{ path: 'something-else', breadcrumb: ':)' },
];

OR: { path: '/', breadcrumb: 'Home' },
Thanks.

breadcrumb prop is being ignored.

My route looks like this:

{
    path: '/careers/:id',
    exact: true,
    breadcrumb: 'test',
    component: CareerDetail
  },

My breadcrumb component:

 <div className="breadcrumbs">
      {breadcrumbs.map((breadcrumb, index) => (
          <span
            className="breadcrumbs__item"
            key={`${breadcrumb.key}-${index}`}>
            <NavLink to={breadcrumb.props.match.url}>{breadcrumb}</NavLink>
          </span>
      ))}
    </div>

The url I navigate to:
http://localhost:3000/careers/5b71927e3e07c3430900118c
What I expect to see in the props:
careers > test
What I see:
careers > 5b71927e3e07c3430900118c

Am I missing something?

Can we use custom Icon for specific routes ?

Can we use custom Icon for specific routes ?

path: '/scenario/', component: ScenarioManagerHome, exact: true , breadcrumbs : 'Scenario', icon:'Test',
	  routes: [
			{ path: '/scenario/create', component: ScenarioModelComposer, exact: true , breadcrumbs : 'Create Scenario', icon:'some-icon'},
			{ path: '/scenario/create/:id?', component: ScenarioModelComposer, exact: true , breadcrumbs : 'Edit Scenario', icon:'some-icon'},
			{ path: '/scenario/details/:id?', component: ScenarioModelDetails, exact: true , breadcrumbs : 'Scenario Details', icon:'some-icon'},
	  ]

How do I add in between crumbs ?

Hey!

I currently work with { disabledDefaults: true }.

this is a route I have:

const routes = [
   { 
       path: '/p/:projectId/environments/:environmentId' , 
       breadcrumb: ({ match }) => { ... return <span>{match.params[...]}</span>} },
   ...
]

Is there a way I could get this breadcrumb ${projectId} > Environments > ${environmentId}

TypeError: Object(...) is not a function

I am trying use this package but I am
getting this error when I try to run the code you have posted in the Example.
screenshot-2017-12-27 honeyguide apps
I am importing Breadcrumbs and rendering <Breadcrumbs/>. Is this not the way to do it?

Translated breadcrumbs (delaying routes init)

Is there a way to delay hoc initialization. Currently I do something like this

const getTranslatedRoutes = (): BreadcrumbsRoute[] => [
  { path: '/logout', breadcrumb: i18n.t('global.menu.logout') },
  { path: '/main', breadcrumb: i18n.t('global.menu.main') },
  { path: '/admin', breadcrumb: i18n.t('global.menu.admin') }
];

export default connect(
  mapStateToProps,
  null
)(withBreadcrumbs(getTranslatedRoutes())(Breadcrumbs));

Which lead to error because app didn't download translations yet. I need route configuration to be lazily initialized.

react-router v6

Hi, I was just trying this with react-router v6 and it seems that v6 does not export matchPath (breaking the breadcrumbs throwing an error).

The readme only states v4 and v5 so I can imagine that this is not yet supported. If this is not an error on my behalf... do you plan to support v6?

dassad

Is there a way to delay hoc initialization. Currently I do something like this

const getTranslatedRoutes = (): BreadcrumbsRoute[] => [
  { path: '/logout', breadcrumb: i18n.t('global.menu.logout') },
  { path: '/main', breadcrumb: i18n.t('global.menu.main') },
  { path: '/admin', breadcrumb: i18n.t('global.menu.admin') }
];

export default connect(
  mapStateToProps,
  null
)(withBreadcrumbs(getTranslatedRoutes())(Breadcrumbs));

Which lead to error because app didn't download translations yet. I need route configuration to be lazily initialized.

How to forward extra props from routes to breadcrumbs?

Hi! I love this package. So simple and yet so powerful. Congratulations.

I'd like to forward some props from my routes array in order to create a responsive breadcrumb. Something like this:

const routes = [
  { path: '/', breadcrumb: null },
  { path: '/app', breadcrumb: null },
  { path: '/app/hello', breadcrumb: 'hello', mobile: true },
  { path: '/app/world', breadcrumb: 'world', mobile: true },
]

It'd be great to have use it to filter out breacrumbs that are not suitable for a mobile screen.

const Breadcrumb = ({ breadcrumbs, mobileScreen }) => {

  const toDisplay = mobileScreen
    ? breadcrumbs.filter(b => b.props.mobile)
    : breadcrumbs

  return (
    <Toolbar>
      {toDisplay.map((breadcrumb, index) => {
        return (
          <React.Fragment key={breadcrumb.key}>
            <Button
              component={Link}
              to={breadcrumb.props.match.url}>
              {breadcrumb}
            </Button>
            {index < breadcrumbs.length - 1 && '/' />}
          </React.Fragment>
        )
      })}
    </Toolbar>
  )
}

It is possible? I've tried to inspect the breadcrumb object without success.

TypeScript: `matchOptions` missing from `BreadcrumbsRoute`

Hi!

Thanks for your work on this HoC and for including TS-support!

I notice the docs and code open for giving matchOptions to each breadcrumb route. However, matchOptions is missing from the type definitions, so if I try to assign the correct type to the options given to withBreadcrumbs I get a type error.

Example:

const breadcrumbs: BreadcrumbsRoute[] = [
  {
    path: '/path/',
    breadcrumb: 'New',
    matchOptions: {
      exact: true,
    },
  },
  {
    path: '/path/:id?',
    breadcrumb: 'Edit',
  },
];

The type checker complains that matchOptions is not defined. A workaround is to remove the BreadcrumbsRoute[] typing, but it would be nice to avoid that.

I can open up a PR that adds the missing definitions if you'd like.

Unable to pass additional props.

When using typescript I am unable to pass down the additional props to component wrapped in withBreadcrumbs method.

interface CustomProps<T> extends InjectedProps<T> {
    rightNode?: ReactNode;
}
const CustomCrumbsHeader = (props: CustomProps<any>) => {
..
}

export default  withBreadcrumbs(breadcrumbsConfig, { disableDefaults: true })(CustomCrumbsHeader);
// following prop 'rightNode' is NOT accepted.
<DefaultedCustomCrumbsHeader rightNode={null} /> 

error message:

Type '{ rightNode: null; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<Pick<InjectedProps<unknown>, never>, any, any>> & Readonly<...> & Readonly<...>'.
  Property 'rightNode' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<Pick<InjectedProps<unknown>, never>, any, any>> & Readonly<...> & Readonly<...>'.  TS2322

Question: Am I doing something wrong here? How to properly pass the additional props?

The only way I've found it to work is to proxy the props with spread:

interface CustomPropsNew {
    rightNode?: ReactNode;
}
const A = withBreadcrumbs(breadcrumbsConfig, { disableDefaults: true })(CustomCrumbsHeader);
const B = (props: CustomPropsNew) => <A {...props} />;

export default B;
// 'rightNode' is now accepted.
<DefaultedCustomCrumbsHeader rightNode={null} /> 

Dependencies:

..
"react": "^16.13.1",
"react-router-breadcrumbs-hoc": "^3.2.10",
"react-router-dom": "^5.1.2",
// devDependencies
"typescript": "^3.5.2"
..

Issues with React.memo

When a component is passed in as the breadcrumb property of a route, and that component is wrapped with React.memo (such as any component using React-Redux v7), this library blows up with "Objects are not valid as a React child".

I think there's a react-is library which may be helpful in detecting these components and handling them correctly.

How to match 404 page?

In my application, I've got a catch-all route for 404 pages, <Route component={Page404} /> and I'm wondering how I would use this HOC for a custom breadcrumb with the 404 page without having to define all of my routes in the withBreadcrumbs function.

Is this even possible?

A bread didn't show

Hi @icd2k3 ,I followed the guide and realize breadcrumbs as below.
I met the problem the fourth bread didn't show.
Any where has problem?

Thanks.

image

const UserBreadcrumb = ({ match }) => ( <span>{getProductName(match.params.pid)}</span> )
const routes = [
{ path: '/product/detail/:pid', breadcrumb: UserBreadcrumb },
{ path: '/', breadcrumb: 'HomePage' },
{ path: '/carts', breadcrumb: 'Cart' },
{ path: '/login', breadcrumb: 'Login' },
{ path: '/product/detail', breadcrumb: 'Detail Info'},
{ path: '/product', breadcrumb: 'Product' }
];

function getProductName(id) {
let callURL = 'https://easy-mock.com/mock/5b14997f27efb177b0e1052f/products/detail/' + id;
axios.get(callURL)
.then(function(response){
console.log(response);
return response.data.product.title;
});
}

const Breadcrumbs = ({ breadcrumbs }) => (

<div id="breadcrumb" className="breadcrumb">
	<ul className="clearfix">
			{breadcrumbs.map((breadcrumb, index) => (				
				<li key={breadcrumb.key}>
						<NavLink to={breadcrumb.props.match.url}>
							{breadcrumb}
						</NavLink>
						{(index < breadcrumbs.length - 1) ?  ">" : ""}
				</li>

			))}
	</ul>
</div>

);

export default withBreadcrumbs(routes)(Breadcrumbs);

Unable to get breadcrumb override working.

I seem to be unable to get the dynamic breadcrumb to work. I've provided my breadcrumb component as well as my routes array. I've added several test dynamic breadcrumbs but they're ignored. When I console.log to crumb in the component the default crumb.breadcrumb is used.

Am I setting this up incorrectly?

/**
 * Breadcrumbs.js
 */

import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { NavLink } from 'react-router-dom';
import withBreadcrumbs from 'react-router-breadcrumbs-hoc';
import { Heading, Pane } from 'evergreen-ui';

import routes from 'containers/App/utils/routes';

const BreadCrumbs = ({ breadcrumbs }) => (
  <Pane alignItems="center" flex={1} display="flex">
    {breadcrumbs.map((crumb, i, arr) => {
      const isLastElement = arr.length - 1 === i;

      let LinkElement = Fragment;
      let props = {};

      if (!isLastElement) {
        LinkElement = NavLink;
        props = { href: crumb.match.url, to: crumb.match.url };
      }

      return (
        <Fragment key={crumb.match.url}>
          <LinkElement {...props}>
            <Heading
              color={isLastElement ? '#234361' : '#1070ca'}
              marginLeft={3}
              marginRight={3}
              size={400}
            >
              {crumb.breadcrumb}
            </Heading>
          </LinkElement>

          {!isLastElement && '/ '}
        </Fragment>
      );
    })}
  </Pane>
);

BreadCrumbs.propTypes = {
  breadcrumbs: PropTypes.array.isRequired,
};

export default withBreadcrumbs(routes)(BreadCrumbs);
/**
 * routes.js
 */

import React from 'react';

import GuestRoute from 'components/routes/GuestRoute';
import UserRoute from 'components/routes/UserRoute';

import LoginPage from 'containers/LoginPage/loadable';
import ForgotPasswordPage from 'containers/ForgotPasswordPage/loadable';
import ResetPasswordPage from 'containers/ResetPasswordPage/loadable';
import ActivateAccountPage from 'containers/ActivateAccountPage/loadable';

import CustomersPage from 'containers/CustomersPage/loadable';

import ReportsPage from 'containers/ReportsPage/loadable';
import ReportsDetailsPage from 'containers/ReportsDetailsPage/loadable';

import SystemsPage from 'containers/SystemPage/loadable';
import SystemDetailsPage from 'containers/SystemDetailsPage/loadable';
import SystemActivitysPage from 'containers/SystemActivityPage/loadable';

import UsersPage from 'containers/UsersPage/loadable';
import UserDetailsPage from 'containers/UserDetailsPage/loadable';
import UserActivityPage from 'containers/UserActivityPage/loadable';

import FormsPage from 'containers/FormsPage/loadable';
import FormManagementPage from 'containers/FormManagementPage/loadable';
import FormSubmitPage from 'containers/FormSubmitPage/loadable';

import SettingsPage from 'containers/SettingsPage/loadable';
import LogoutPage from 'containers/LogoutPage';
import NotFoundPage from 'containers/NotFoundPage/loadable';

import MainLayout from './../layouts/MainLayout';
import EmptyLayout from './../layouts/EmptyLayout';

const userNamesById = { '016b18b1-41db-49ae-b15a-8cdaf5d78945': 'John' };

const DynamicUserBreadcrumb = ({ match }) => (
  <h1>{userNamesById[match.params.userId]}</h1>
);

const routes = [
  {
    path: '/login',
    exact: false,
    component: LoginPage,
    layout: EmptyLayout,
    type: GuestRoute,
  },
  {
    path: '/forgot-password',
    exact: false,
    component: ForgotPasswordPage,
    layout: EmptyLayout,
    type: GuestRoute,
  },
  {
    path: '/reset-password/:token',
    exact: false,
    component: ResetPasswordPage,
    layout: EmptyLayout,
    type: GuestRoute,
  },
  {
    path: '/activate-account/:token',
    exact: false,
    component: ActivateAccountPage,
    layout: EmptyLayout,
    type: GuestRoute,
  },
  {
    path: '/customers',
    exact: false,
    component: CustomersPage,
    layout: MainLayout,
    type: UserRoute,
  },
  {
    path: '/reports',
    exact: true,
    component: ReportsPage,
    layout: MainLayout,
    type: UserRoute,
  },
  {
    path: '/reports/:id',
    exact: false,
    component: ReportsDetailsPage,
    layout: MainLayout,
    type: UserRoute,
  },
  {
    path: '/systems',
    exact: true,
    component: SystemsPage,
    layout: MainLayout,
    type: UserRoute,
    breadcrumb: 'test',
  },
  {
    path: '/systems/:id',
    exact: false,
    component: SystemDetailsPage,
    layout: MainLayout,
    type: UserRoute,
  },
  {
    path: '/systems/:id/activity',
    exact: true,
    component: SystemActivitysPage,
    layout: MainLayout,
    type: UserRoute,
  },
  {
    path: '/users',
    exact: true,
    component: UsersPage,
    layout: MainLayout,
    type: UserRoute,
  },
  {
    path: '/users/:id',
    exact: true,
    component: UserDetailsPage,
    layout: MainLayout,
    type: UserRoute,
  },
  {
    path: '/users/:id/activity',
    exact: true,
    component: UserActivityPage,
    layout: MainLayout,
    type: UserRoute,
  },
  {
    path: '/forms',
    exact: true,
    component: FormsPage,
    layout: MainLayout,
    type: UserRoute,
  },
  {
    path: '/forms/:id',
    exact: false,
    component: FormManagementPage,
    layout: MainLayout,
    type: UserRoute,
    breadcrumb: DynamicUserBreadcrumb,
  },
  {
    path: '/submit/:id',
    exact: false,
    component: FormSubmitPage,
    layout: MainLayout,
    type: UserRoute,
  },
  {
    path: '/settings',
    exact: false,
    component: SettingsPage,
    layout: MainLayout,
    type: UserRoute,
  },
  {
    path: '/logout',
    exact: false,
    component: LogoutPage,
    layout: MainLayout,
    type: UserRoute,
  },
  {
    component: NotFoundPage,
    layout: MainLayout,
    type: UserRoute,
  },
];

export default routes;

Question about type definition

How would you properly typedef the props that go into the following example you displayed below?

I notice that your just doing a decomposition, but I want to pass in as props.

import withBreadcrumbs from 'react-router-breadcrumbs-hoc';

const Breadcrumbs = ({ breadcrumbs }) => (
  <React.Fragment>
    {breadcrumbs.map(({ breadcrumb }) => breadcrumb)}
  </React.Fragment>
)

export default withBreadcrumbs()(Breadcrumbs);

So this instead

import withBreadcrumbs from 'react-router-breadcrumbs-hoc';

const Breadcrumbs = (props: any) => (
  <React.Fragment>
    {props.breadcrumbs.map(({ breadcrumb }) => breadcrumb)}
  </React.Fragment>
)

export default withBreadcrumbs()(Breadcrumbs);

I can't figure out what the any would be from the type definition.

Same question for

breadcrumb: String|Function? // if not provided, a default breadcrumb will be returned

For the Function, what is the type of the parameters of the function being passed in

Thanks,
Derek

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.