How to get the current url path using hook router in react? - reactjs

I'm new to react and react hooks. I'm using hookrouter package and I tried to googling about the question, but didn't find much about it.
What I want?
I'm trying to get the current url path. For e.g. for https://example.com/users/temp, I want to get /users/temp.
Any help will be appreciated. Thanks in advance!

I finally found on github doc.
import {usePath} from 'hookrouter';
const PathLabel = () => {
const path = usePath();
return <span>Your current location: {path}</span>;
}

I wanted to let you know there's also a generic way to return the path using Javascript, in case you're not using hookrouter on a different project
Just invoke window.location.pathname
I'm using it for setting active className for links in my Nav component based on which route the client is on using a ternary expression. Example:
const NavList = props => {
return (
<NavContainer>
<ul>
{routes.map(({ key, href, label }) => (
<A href={href} key={key} className='route-link'>
<li
className={href === window.location.pathname ? 'active' : null}
>
{label}
</li>
</A>
))}
</ul>
</NavContainer>
);
};

Related

How to get current hash route in single page website

I have a single-page web portfolio in which I have a navbar like the following code:
const Nav = ({ isToggled, onToggle }) => {
const [activeNav, setActiveNav] = useState('#')
return (
<nav>
<a
href='/#'
onClick={() => setActiveNav('#')}
className={activeNav === '#' ? 'active' : ''}
>
<AiOutlineHome />
</a>
<a
href='#about'
onClick={() => setActiveNav('#about')}
className={activeNav === '#about' ? 'active' : ''}
>
<AiOutlineUser />
...
</nav>
)
}
I have hyperlink tags to navigate through the website. However, I would like to update the active nav icon according to the current place the user is in. I found something called useLocation() from react-router-dom, but I currently do not use this package. Is it necessary? What should I do to achieve my goal? Thanks in advance for any help you can provide.
A really simple solution I could think of would be combining the use ScrollTrigger plugin from GSAP and React useEffect, they have really great documentations on their site! But this is the most basic example, say if you just want the nav link to show an active state if the section enters the viewport
useEffect(() => {
gsap.to(".nav-link-className", {
scrollTrigger: "#id",
// active state styling here
});
})
EDIT:
Since I do not use GSAP myself that often, there may be a much better way of going about this within GSAP, but here is an example on CodeSandBox, this should at least get you started. Note that one limitation (or at least my own limitation due to lack of knowledge with GSAP) is that I would have to hard code the animation when the trigger element leaves the DOM
The solution I found was to use the react-scroll package. I just switched my a tags for Link from react-scroll.
This:
<a
href='/#'
onClick={() => setActiveNav('#')}
className={activeNav === '#' ? 'active' : ''}
>
<AiOutlineHome />
</a>
For this:
<Link
activeClass='active'
to={'header'}
spy={true}
duration={500}
offset={-100}
>
<AiOutlineHome />
</Link>
In App.jsx, wrapped each component in Element from react-scroll (e.g. <Element name={'portfolio'}><About /></Element>).

How to do simple ternary in React with TypeScript?

I'm new to React and TypeScript. I cannot find a solution for this on Google.
I have a component that outputs an image. I want to wrap that image in an <a> tag if there is a given URL prop. I am unsure how to write the logic simply enough.
From looking online I have written the following:
const imageBlock = ({
imageSrc,
imageAlt,
imageTitle,
imageLink,
}: imageBlockProps) => {
return (
imageLink && (
<a href={imageLink}>
)
<img src={imageSrc} alt={imageAlt} title={imageTitle} />
imageLink && (
</a>
)
)
};
But I cannot get it to work.
Would anyone know the best way to do it?
There is nothing wrong in writing imperative code, really:
const img = <img src={imageSrc} alt={imageAlt} title={imageTitle} />;
if (imageLink) {
return <a href={imageLink}>{img}</a>;
}
return img;
As #zerkms said, there's nothing wrong with an if statement.
But... what you could do is something like this:
const ConditionalLinkWrapper = ({ link, wrapper, children }) =>
link ? wrapper(children) : children;
Your code then would use this reusable component like this:
const imageBlock = ({
imageSrc,
imageAlt,
imageTitle,
imageLink,
}: imageBlockProps) => (
<ConditionalLinkWrapper
link={imageLink}
wrapper={children => <a href={imageLink}>{children}</a>}
>
<img src={imageSrc} alt={imageAlt} title={imageTitle} />
</ConditionalLinkWrapper>
);
In my opinion, it's cleaner and easier to read than with an if statement, but that might not be the case for everyone!
I also like the fact that this is following the Composition vs Inheritance pattern.
You can also reuse the ConditionalLinkWrapper component anywhere in your code if you need to.
This is not recommended, but if you're really looking for a one-liner, check this out:
return ((img = <img src={imageSrc} alt={imageAlt} title={imageTitle} />) =>
imageLink
? <a href={imageLink}>{img}</a>
: img
)()

React State Hook - toggle a class

I'm trying to build a sidebar navigation menu and thought I'd take advantage of the new State hook in React. I've read the docs but can't seem to find an example similar to what I need, which is quite simply to toggle a CSS class on click which will in turn open and close my menu.
Here's what I've tried:
const SidebarMenuItem = ({ component }) => {
const [ menuActive, setMenuState ] = useState(false);
return (
<li className="p-sidebar-menu-item">
menuActive:
{ menuActive }
<button className="p-sidebar-menu-item__link" onClick={() => setMenuState(!menuActive)}>{ component.component }</button>
{ component.children && (
<ul className="p-sidebar-menu">
<li><a href={`/${component.slug}`}>Overview</a></li>
{ component.children.map((subPage, key) => (
<li key={ key }>
<a href={`/${subPage.slug}`}>{ subPage.name }</a>
</li>
))}
</ul>
)}
</li>
)
}
export default SidebarMenuItem;
Any ideas where I'm going wrong?
Thanks
Just make the className dynamic, so instead of setting
<li className="p-sidebar-menu-item">
transform it in a template literal
<li className={`p-sidebar-menu-item`}>
and then add your class conditionally (the "yellow" class in my example)
<li className={`p-sidebar-menu-item ${menuActive ? "yellow" : ""}`}>
Take a look at this CodeSandbox: here I've just added your component and changed the way the className attribute is generated.
If you want to avoid the ternary operator you could use the classnames module and then update your code to
import c from "classnames";
...
...
...
<li className={c("p-sidebar-menu-item", {yellow: menuActive})}>
Another clean solution can be to generate the className string in advance, for example
let classes = "p-sidebar-menu-item";
if(menuActive) {
classes += " yellow";
}
<li className={classes}>
Let me know if you need some more help 😉
I think you just need
const [ menuActive, setMenuState ] = useState(false);
change the name of setState to setMenuState in your code also
Don't forget to use the prevState or you can have a bug.
<button
className="p-sidebar-menu-item__link"
onClick={() => setMenuState((prevMenuActive) => !prevMenuActive)}>
{component.component}
</button>

How to create dynamic hyperlink (href) using React?

Completely new to React, so please forgive my ignorance.
Using ReactStrap for this example. I'm attempting to define {url} dynamically, to where it's defined under src/components in its own jsx file:
const NavLink = ({ url },{ children }) => (
<RSNavLink href={url}>
{children}
</RSNavLink>
);
but when I call the component elsewhere, I can insert whatever hyperlink I'd like:
<NavLink href="/">Hello, World!</NavLink>
I'm not able to find documentation specific to what I'm looking for (or at least, I may not be searching for the right thing). If anyone could point me in the right direction, it'd be much appreciated.
In NavLink Component you are wrongly destructing props value and give exactly same name as props. change it like this
const NavLink = ({ url ,children }) => (
<RSNavLink href={url}>
{children}
</RSNavLink>
);
<NavLink url="#">
Hello World
</NavLink>
check demo in stackblitz

Gatsby page in page

I'm using gatsby js and trying to figure out how to have a page level side bar with Gatsby links that render a new component inside a div in the same page I can do this using react-router-dom but in Gatsby all I can find is how to create blog posts which is driving me nuts as every tutorial I find is the same blog post.
Here is my layout page /layouts/index.js
export default ({ children }) => (
<div id="layout">
<header>
<h3>Header</h3>
<MainNav />
</header>
{children()}
</div>
)
About Page
/pages/about.js
export default ({ location, match }) => {
console.log('location = ', location, 'match = ', match );
return (
<div id="about">
<SideBar />
<div id="content">
// . add child template or component for link clicked in sidebar
</div>
</div>
);
};
What I'm trying to do is when a user clicks on a link in the side bar stay on about but render a new component or template based on the gatsby-link clicked in the about sidebar.
The About SideBar component
/components/about/side-bar.js
const SideBar = () => {
return (
<div id="side-bar">
{/* <li><Link to='/about?sort=name'>work</Link></li> */}
<li><Link to="/about/work">work</Link></li>
<li><Link to='/about/hobbies'>hobbies</Link></li>
<li><Link to='/about/buildings'>buildings</Link></li>
</div>
)
}
Problem with the links above, they are trying to go to a new page called.
/about/work
This is not what I'm trying to do. Again I'm trying to make it stay on about but render a new component inside the content div.
Please help gatsby is so all over the place as far as docs goes. ok maybe its just me and not getting the docs clearly.
Thanks
UPDATE:
I tried adding a page suing createPage which works for me kind of but it doesn't pass the match.params id
gatsby-node.js
exports.createPages = ({ boundActionCreators }) => {
const { createPage } = boundActionCreators;
const myComponent = path.resolve('src/pages/about/index.js');
createPage({
path: '/about/:id',
component: myComponent
})
}
After a long time of trying to understand Gatsby and I can say I still don't as its docs are vast and not very clear. But once I started to look at the node-apis and onCreatePage it gave me some ideas. This is what the docs literally say.
onCreatePage
Called when a new page is created. This extension API is
useful for programmatically manipulating pages created by other
plugins e.g. if you want paths without trailing slashes.
So the only part in here that gives me a hint of this might be the key to helping me is this line. useful for programmatically manipulating pages created by other
plugins
Anyway this got me writing some code at least. Then about 3 hours later I found a plugin that was doing exactly what I was trying to do with this method. The plugin is called gatsby-plugin-create-client-paths key here is client-paths!!!!!
This makes life worth living! So in my case above I just wanted to be able to use Gatsby's router ( which is just react-router-dom behind the scenes), to pass me and id or value to routers match.params object. It still doesn't but what it does do is checks for any path after a prefix like /folder/ in my case '/about/work and recreate the page with a template component (in my case keep using pages/about/index.js), which is my template. Now that we have about/index.js rendering for ever link after /about/ then we can use some internal switch statement to handle the location that is been passed to /about/index.js. Still don't get match.params update but I do get props.location.pathname; which allows me to extract everything after the prefix to use in a switch statement to render my specific components based on the routes pathname. Enough rabbiting on here is a rough solution to show as an example.
So add the plugin as an npm install.
open up gatsby.config.js and add the below code to the exports.
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-create-client-paths`,
options: { prefixes: [`/about/*`] },
},
]
}
Then in my main about page pages/about/index
import React from "react";
import SideBar from '../../components/about/side-nav';
export default (props) => {
const { pathname } = props.location;
var n = pathname.lastIndexOf('/');
var pageId = pathname.substring(n + 1);
const page = () => {
switch(pageId){
case '':
return (
<div>Work Page</div>
);
case 'work':
return (
<div>Work Page</div>
);
case 'hobbies':
return (
<div>Hobbies Page</div>
);
case 'buildings':
return (
<div>buildings Page</div>
);
}
}
return (
<div id="about">
<SideBar />
<div id="content">
{page()}
</div>
</div>
);
};
Then in my sidebar I call it like this.
<li><Link to="/about/work">work</Link></li>
<li><Link to='/about/hobbies'>hobbies</Link></li>
<li><Link to='/about/buildings'>buildings</Link></li>
Hopefully this will help someone else out. After all this I'm starting to really question the bulk of gatsby especially with docs not been very clear. Based on the response to my question I guess not many people in stackoverflow's community are using Gatsby which is worrying when you need help. It does look like Gatsby's github community is very helpful but that should be for bug issues and not for questions like mine, but encouraging to see.
Hope this helps someone.

Resources