How to get current hash route in single page website - reactjs

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>).

Related

Warning: validateDOMNesting(…): <a> cannot appear as a descendant of <a>

I'm using React router dom Link component. It is basically twitter's home feed. I want to be able to have two type of Links in one div component. One will be Link to go to user's profile and other one to go to post. I am currently getting warning and couldn't find solution for now. Here is the screenshot as reference:
I understand the issue here, my post Link is the parent element and I've added two user Link components inside of it as the user should be able to access post page when he clicks on anything inside of the post except user's profile photo and user's name. Is there any smarter way of achieving this and keeping links like this?
Code:
{posts?.map((post) => (
<Link
className={classes.link}
key={post.user.id}
to={`/posts/${post.id}`}
>
<div className={classes.post}>
<Link to={`/users/${post.user.id}`}>
<img
className={classes.profileImage}
src={DefaultLogo}
alt="default-profile"
/>
</Link>
<article className={classes.postDetails}>
<Link
className={classes.link}
to={`/users/${post.user.id}`}
>
<Typography
variant="subtitle1"
className={`${classes.postTitle} ${classes.content}`}
>
{post.user.name}
</Typography>
</Link>
<Typography
variant="subtitle1"
className={`${classes.postText} ${classes.content}`}
>
{post.body}
</Typography>
</article>
</div>
</Link>
))}
Yes, having anchor tags inside of another anchor tag is misleading a bad approach to doing things. But given your requirements you can make use of a basic button with react router dom history api.
A simple example:
import {Link, useHistory} from 'react-router-dom'
const App = () => {
const history = useHistory()
return (
<a
href='/user/1'
>
<h2>John doe</h2>
<div>here are some use information</div>
{/* Need to prevent the event bubbling */}
<Link role='link' to='/users/1/posts'>
User posts
</Link>
</div>
)
}

React: Conditional className is not updated in DOM

When conditions changes (location) react doesn't update the classname in dom, although it looks changed in react developer tools. Simplified code is something like this:
<Menu>
{appRoutes.map(route => {
console.log(
pathname,
route.path,
pathname === route.path,
pathname === route.path ? 'ant-menu-item-selected' : '',
);
return (
<Menu.Item
key={route.name}
className={pathname === route.path ? 'ant-menu-item-selected' : ''}
onClick={() => {
setOpenKeys([getKey(route.name, index)]);
if (app.mobile) app.toggleMobileDrawer();
}}
>
<Link to={route.path}>
<span>
<span className="mr-auto">{capitalize(route.name)}</span>
</span>
</Link>
</Menu.Item>
);
})}
</Menu>
pathname variable is from location. I use useLocation hook from react router so when location changes, component re-renders properly. console.log prints all vars as it should be. Further, when I check react developer tool, classname looks as it should be:
But when checking the elements in developer tools, class is not updated for the same element in dom:
When I refresh the page (not changing the location), it renders properly, and class name is removed from dom. So how can I force react to update the classname in dom?
Using react 16.13.1, antd components.
One approach is to use the selectedKeys property from Antd Menu and set this to your current route path value.
selectedKeys: Array with the keys of currently selected menu items
The menu may look like this:
<Menu
activeKey={props.currentPath}
mode="inline"
selectedKeys={props.currentPath}
style={{ height: "100%", borderRight: 0 }}
>
Additional examples can be found in antd's github thread for >how to use sider with react-router links<.

Is It possible to change the property from hover to click during a specific break point using media queries in React?

Right now my li tag has a mouseenter and mouseleave effect, but when I shrink my site down to mobile view, I want it to change to onClick instead.
So the code below shows my menu with the dropdown. Whenever I click on the hamburger menu in mobile view, it will trigger the active class on the <ul> and open up the mobile view. Then when I hover over the li tag it will display the dropdown menu.
Instead of it displaying when I hover, I need it to display when I click the ul tag only at the breakpoint of 960px.
<ul className={click ? 'nav-menu active' : 'nav-menu'}>
<li
className='nav-item'
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
<Link to='/' className='nav-links' onClick={() => setClick(false)}>
Home <i class='fas fa-caret-down' />
</Link>
{dropdown && <Dropdown />}
</li>
<li className='nav-item'>
<Link
to='/services'
className='nav-links'
onClick={() => setClick(false)}
>
Services
</Link>
</li>
</ul>
Is that something that is posssible to do? Or would I have to completely redo my code?
It most definitely is. And there are many options of how you could do it, these are some that come to my mind at first:
Option #1:
You could render 2 completely different things based on media query, something like this:
<div className="hidden-xs">
<ol onClick={() => {}}></ol>
</div>
<div className="hidden-lg">
<ol onMouseLeave={() => {}} onMouseEnter={() => {}}></ol>
</div>
Option #2:
You could disable certain functions based on current viewport
const onMouseEnter = () => {
if (window.width > something) {
return;
}
do something otherwise
}
const onClick = () => {
if (window.width < something) {
return;
}
do something otherwise
}
I would recommend taking a look at some of these articles & modules:
Medium post about media queries in React
react-media
react-responsive

React Web App Routing Works on Desktop (incl. Mobile Dev View), but not on Mobile Browsers

I'm really stuck on a problem with a React web app I'm building. I'm using the array.map() method to dynamically create dropdowns, and I'm finding that everything works beautifully on desktop browsers (including in the mobile view of Chrome dev tools), but not on actual mobile browsers. I would really appreciate your thoughts!
Expected Behavior
I expect the dropdowns to populate, usually from an array of objects. Then, when the user clicks on one of the items in the dropdown menu, it will either trigger (1) a function or (2) a link to another part of the React App. Note that I'm using react-router-dom to handle routing.
Observed Behavior on Mobile Browsers
The dropdowns populate correctly, but they malfunction when I select from among the options (see Figure 1).
This behavior is observable when the dropdown selection triggers both a (react-router-dom Component) and calls a function. In the limited cases when the function is called correctly, the correct parameters are passed and the function does execute correctly.
Code Snippits
This is an example of the code I'm using to generate the list of links. It is a simple React functional component that serves as the header to all settings pages in my app, and the Component is part of a React MaterializeCSS library, which seems to be working fine.:
const SettingsHeader = () => {
let { url } = useRouteMatch();
const { userAccess, styleItem, headerStyle, updateHeader, theme } = useContext(SettingsContext);
const options = userAccess.length ? (
userAccess.sort().map(permission => {
// Returns an object with details needed to build the component via a Settings Context function.
let details = styleItem(permission);
return (
<Link
key={permission}
to={`${url}${details.link}`}
onClick={() => updateHeader(permission)}
>
<Icon className={theme.text}>{details.icon}</Icon>
<span className={theme.text}> {details.text}</span>
</Link>
);
})
) : (
<h4 className="grey-text">
<Icon>warning</Icon> You don't have permission to edit any settings.
</h4>
);
return (
<h4 className={theme.text}>
<i className="material-icons">{headerStyle.icon}</i> {headerStyle.text}
<Dropdown
options={{ ... }}
trigger={
<Button className={"right " + theme.themeColor} node="button">
Views
</Button>
}
>
{options}
<a href="#!">
<Icon className="grey-text">close</Icon>
<span className="grey-text"> Close</span>
</a>
</Dropdown>
</h4>
);
}
This is an example of the code I'm using to generate a list of theme options, each of which calls a function in a React Context Component I'm using in many places in the App:
const ThemeSettings = () => {
// Brings in Theme update function from SettingsContext
const { changeTheme, theme } = useContext(SettingsContext);
// Array of possible themes.
const themesList = ['Burnt Orange', 'Chrome', 'Deep Purple', 'Earth', 'Fresh Green', 'Green', 'Maroon', 'Navy', 'Pink', 'Red', 'Royal Blue', 'Teal']
const themeOptions = themesList.map(theme => {
let themeObject = getTheme(theme);
return (
<a href="#!" key={theme} onClick={(e) => changeTheme(e, theme) }>
<Icon className={themeObject.text}>style</Icon>
<span className={themeObject.text}> {theme}</span>
</a>
)
});
return (
<Dropdown
options={{ ... }}
trigger={
<Button className={"left " + theme.themeColor} node="button">
Themes
</Button>
}
>
{ themeOptions }
<a href="#!">
<Icon className="grey-text">close</Icon>
<span className="grey-text"> Close</span>
</a>
</Dropdown>
);
}
Thanks very much for giving this a look!
This is a known MaterializeCSS bug for devices running iOS-13.
I was only able to find one workaround available at time of writing, mentioned by Falk in this issue thread:
To fix it, I had to check out the #v1-dev branch of
https://github.com/Dogfalo/materialize, build it with grunt release
and use the materialize.js in /dist folder for my project.

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

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

Resources