How to give active class on NavItem on browser back - reactjs

In my application I am using react router 3
For navigation menu i have used
<nav>
<NavItem> </NavItem>
</nav>
It is working fine on click of nav menu but when going back using browser back, the url changed but the active class does not applied.

I will try to explain how it should be done generally, not framework specific. you can implement the same in any framework after that
in order to make stateful navigation, You can take advantage of query parameters.
all you have to do on click of the nav you have to append some value to the query parameters which will identify its uniqueness.
and on the component initialization you have to read the query params and programmatically select that tab.(apply css class to it)
For example, you have:
url:somedomain.com/home -- this is where you have navigation
NavItem1 href="somedomain.com/home?nav=tab1"
NavItem2 href="somedomain.com/home?nav=tab2"
NavItem3 href="somedomain.com/home?nav=tab3"
when the component initializes , you have to read the query params if exists and give the appropriate class to the particular NavItem

Related

GraphQL in Gatsby.js - use the result of a query, to make the second query combined into one [duplicate]

I am using Gatsby as a frontend to a WordPress backend. I have successfully fetched the menu items but I now need to link them to the pages I have created. I have added the Menu Links in my Gatsbyconfig.js but I have no idea on how I can go about mapping it to the menu items at the same time as the menu itself. It ends up contradicting each other. Is this possible to do? I am quite new at graphQL. Never touched it till this project. Below is the GraphQl Query
{
allWpMenuItem(filter: {menu: {node: {name: {eq: "Navigation Menu"}}}}) {
edges {
node {
label
}
}
}
site {
siteMetadata {
title
menuLinks {
name
link
}
}
}
}
The label holds the name for each menu and the link holds the link to the pages I have created. I am trying to fix this into this bit of code
<div>
{props.allWpMenuItem.edges.map(edge =>(
<a href="#" key={edge.node.label}>
{edge.node.label}
</a>
))}
</div>
I am trying to query the menu links change the anchor tag to the link item and then point it to the menu link.
The short answer is that you can't, natively each query is a separate entity. However, you can combine queries using createResolvers, for further details check: https://github.com/gatsbyjs/gatsby/discussions/28275 (thanks LekoArts for the feedback).
However, you have a few approaches to bypass this limitation. Given that each query is transformed into a standalone JavaScript object, you can always manipulate them using native JavaScript filtering or doing whatever you need. The idea is to create an empty object and populate it with links and labels, in a single loop or using two different, then, in your component, iterate through this object instead of through the props like you are doing now.
I can't create a minimal example without knowing the internal structure or without knowing what's inside menuLinks.name.
By the way, if you are using internal navigation I would hardly suggest using <Link> component, among other things, using the #reach/router (extended from React's router) will only render the necessary parts of each component, creating smoother navigation than using native anchor tag, what will refresh all the page.

React Bootstrap Nav.Link href/onSelect issues

I am using React Bootstrap NavLink to main navigation. What I'm trying to do is:
Allow opening in new tab with correct route (no checking if it should be left needed)
If user wants to change current tab page - check if it should be left (eg some changes on current page may not be saved)
My current version looks like this:
{paths && paths.map(path =>
<NavLink href={path} onSelect={this.handleOnSelect}>
{path}
</NavLink>
}
And works quite alright, but the problem starts when my url looks like domain.com/a/b. Let's assume I click on c, navLink creates route domain.com/a/c instead of domain.com/c. I tried using href={`/${path}`} and routing worked fine but it was ignoring onSelect.
Does anyone have an idea how this could be solved? Is that even possible to accomplish?

How to pass a set of React Nodes to unrelated components

I have a question about React, here's a simplified version of a React app.
In the app I want to render a fixed primary menu and a secondary menu that is optional and its content is controlled by inner components rendered in routing.
Also secondary menu is rendered somewhere else in mobile version of the app.
function App() {
return <Router>
<PrimaryMenu/>
<SecondaryMenu/>
<LayoutContent/>
{/* This block is rendered only on mobile devices */}
<Responsive {...Responsive.onlyMobile}>
<SecondaryMenu/>
</Responsive>
</Router>;
}
LayoutContent will render actual page content (using a Page component) according to routing rules and every page component may render its own secondary menu like this (e.g. page1 has its own submenu, page2 has another one, page3 has not.)
<Page title='Page 1 - With secondary menu'>
<SecondaryMenuItems>
{/* I want this content as children of secondary menu in both mobile and desktop menubars */}
<li>Page 1 item 1</li>
<li>Page 1 item 2</li>
</SecondaryMenuItems>
</Page>
I tried to implement it by using React Contexts but if I store children nodes in context an infinite render is triggered. I changed it to use a id property in <SecondaryMenuItems/> component but the approach is very fragile and also has some drawbacks.
Here's my working example it's working but as I said is pretty fragile:
What if I use a duplicate id for secondary menus?
What if I forget a secondary menu key?
Also if you switch to a page with a menu and then go to page3 (that has no menu) previous page menu remain on screeen.
How to accomplish this with react? Is there a suggested way to do that?
A simpler way to express my question is "how to pass a set of react nodes between unrelated components (e.g. siblings components)"
Update
I've completed my working example with received hints, now by combining useRef with ReactDOM.createPortal I achieved final result which is now in the example.
This is a use case for React Portals. Portal will let you render secondary menu items from a page into secondary menu container that exists somewhere else
All you need to do is to call React.createPortal in render of thepage, pass rendered element and target node to render into, regardless of position in DOM tree
I've edited your example using portals here https://codesandbox.io/s/secondary-menu-example-vbm3x. This of course is a basic example, you might want to abstract portals logic in a separate component for convenience, and/or pass dom reference from parent, instead of calling getElementById on mount
Rendering same children in multiple sibling nodes
The question asks "how to pass a set of react nodes". Ideally, don't. If you are rendering nodes somewhere in your hierarchy with the intention of using them elsewhere, you may be using the wrong strategy.
If you need to render the same components in different places, make a function that renders the components, and call it from both places. In other words, always pass the information, not the rendered elements.
Render inside the router
In a typical Single Page Application, the router will render all of the (non-static) components. This is how the example should have done it. The routing component (LayoutContent) should have been responsible for rendering the "passed nodes" (SecondaryMenu) directly.
<Route path="/page1">
<Page title="Page 1 - With secondary menu">
<SecondaryMenu id="menu1"> {/* <- use SecondaryMenu instead of SecondaryMenuItems */}
<li>Page 1 item 1</li>
<li>Page 1 item 2</li>
</SecondaryMenu> {/* <- use SecondaryMenu instead of SecondaryMenuItems */}
</Page>
</Route>
When rendering inside the router is impossible
If for some reason the routing component cannot render the content directly, then a Single Page Application (or routing) solution is probably the wrong solution here. The question doesn't include any information as to why the components can't be rendered inside the router, feel free to edit the question and comment with more info.
Another way of achieving the example would be for there to be no routing component (i.e. no LayoutContent) and for SecondaryMenu to check the path of the page and conditionally render the appropriate content based on that.
It may seem silly to manually render conditionally based on a path when there is a router component which does this for you, and I would agree. The solution is then to not use a router at all. Trying to render children in the router and passing them has a strong code smell.
In the React hierarchical layout, if the same information is needed make decisions about rendering in multiple places (the path in this case), move that information up to the nearest parent of all components and pass it down as props or as context.
Avoiding ID clashes
"What if I use a duplicate id for secondary menus?"
If you call a function to render the secondary menu instead of rendering it and passing it, then you can pass a menu prefix in the props, and use this menu prefix in the function.
function SecondaryMenuItems({ children, idPrefix, path }) {
if (path == '/path1') {
return (
<ul id={`${idPrefix}-newlist`}>
On keys
"What if I forget a secondary menu key?"
React keys need only be unique within a rendered list. In fact, keys are simply an optimisation to prevent React having to re-render a generated list on every pass. If you forget to include a key (or make a bad choice of key), React has to re-render the list every time, but it's not more serious than that. In simple cases, you won't notice the drop in performance.

activeClassName not applying to Link when path is descendant to parent

This is my route configuration:
<Link className="nav-link " activeClassName="active" to={'/parent'}>
parent
</Link>
When I visit the url: https://localhost:8000/some-url/#/parent/ react applies the class active to link. But when I visit the path: https://localhost:8000/some-url/#/parent/child , react doesn't apply the active class.
Any suggestion on how to get the active class to be applied on the link when url is any descendent of the parent.
I have followed the documentation which says
The will be active if the current route is either the linked
route or any descendant of the linked route. To have the link be active only on the exact linked route, use instead or set the onlyActiveOnIndex prop.
But not sure why it is not getting applied by default.
Insted of using Link you should use NavLink as the documentation says
A special version of the <Link> that will add styling attributes to
the rendered element when it matches the current URL.
by using NavLink you should be able to achieve this also you can use things like exact, strict, isActive

admin-on-rest Show component with specified id on custom page

I am using admin-on-rest in my React app and wonder how is possible to make a custom page containing Show (or another detail component) with specified resource Id (for example: I want my app to fetch only resource with id = 1)
I don't know if this is canonical or the intended way to do things but you can supply both basePath and record as props to the ShowButton and EditButton components.
You can use this to redirect your user basically anywhere.
It should be possible (using some switch case or dependent input addon) to also add these props to the ListButton in the Show and Edit view and redirect your user back to the originating ListView.
Here is documentation to supply actions to the List view.
https://marmelab.com/admin-on-rest/List.html#actions

Resources