admin管理员组文章数量:1399887
In my menu, I have a navigation link- My Profile. When the user clicks My Profile react redirects to their profile. Then the nav link is set to active as expected, likewise, the isActive
style is applied.
<NavLink className="nav-link" to="/my_profile" activeStyle={isActive}>
My Profile
</NavLink>
A user can navigate to users' profiles by navigating to www.sitename/***username*** (like Twitter or Instagram).
<Route path="/:profile_name">
<Profile />
</Route>
When a user is logged in and navigates to their own profile through this dynamic route, I want the nav link for My Profile to be set to active. But I can only figure out how to set My Profile to active when My Profile is clicked on, not when the user navigates to their own profile through the address bar.
How can I set the NavLink
for My Profile to active when a user navigates to their dynamic profile route through the address bar?
Example
In my code example, I have the currentUser
hard coded in as dash123
, so when we navigate to the
Profile
ponent recognizes /dash123
is the current user's profile and sets the state authorized
to true. When authorized is true
, Profile
renders to display "Edit your profile". Can I also make it so when authorized
is true
, the NavLink
for My Profile is set to active?
CodeSandbox
Code:
import React, { useState, useEffect } from "react";
import {
NavLink,
BrowserRouter as Router,
Route,
Switch,
useParams
} from "react-router-dom";
function Nav() {
const isActive = {
fontWeight: "bold",
backgroundColor: "lightgrey"
};
return (
<ul className="navbar-nav mr-auto">
<li className="nav-item">
<NavLink className="nav-link" to="/Shop" activeStyle={isActive}>
Shop
</NavLink>
</li>
<li className="nav-item">
<NavLink className="nav-link" to="/my_profile" activeStyle={isActive}>
My Profile
</NavLink>
</li>
</ul>
);
}
function Shop(props) {
return (
<div>
Lots of items to buy
<br /> Shoes $4.99
<br /> Carrots $9.99
<br /> Teslas $800,000
</div>
);
}
function Profile(props) {
let { profile_name } = useParams();
const [profile, setProfile] = useState();
const [authorized, setAuthorized] = useState(null);
// fake calls to database
const currentUser = () => {
return { name: "Dashie" };
};
const userInfo = (username) => {
const db = [
{ username: "dash123", name: "Dashie" },
{ username: "bob123", name: "Bob" }
];
return db.find((userDoc) => userDoc.username === username);
};
useEffect(() => {
if (props.profile === "currentUser") {
setProfile(currentUser());
} else {
setProfile(userInfo(profile_name));
}
}, [profile_name, props.profile]);
useEffect(() => {
if (profile && profile.name === currentUser().name) {
setAuthorized(true);
}
}, [profile]);
return (
<div>
Profile:
<br />
{profile && <>Name: {profile.name}</>}
<br />
{authorized && profile && <>Edit your profile, {profile.name}</>}
</div>
);
}
export default function App() {
return (
<div className="App">
<Router>
<Nav />
<Switch>
<Route path="/shop">
<Shop />
</Route>
<Route path="/my_profile">
<Profile profile={"currentUser"} />
</Route>
<Route path="/:profile_name">
<Profile />
</Route>
</Switch>
</Router>
<h2>Problem</h2>
<p>
If you click on <b>Shop</b>, as expected the <b>Shop</b> nav link will
be highlighted.
<br />
<br /> Likewise, the <b>My Profile</b> nav link will be highlighted when
we click on it and navigate to the user's profile.
<br />
<br /> But, we also want to have <b>My Profile</b> highlighted when we
navigate to,{" "}
<i>
"https://whateverdomainyouron/<b>dash123</b>"
</i>{" "}
since this takes us to the current user's profile.
</p>
</div>
);
}
In my menu, I have a navigation link- My Profile. When the user clicks My Profile react redirects to their profile. Then the nav link is set to active as expected, likewise, the isActive
style is applied.
<NavLink className="nav-link" to="/my_profile" activeStyle={isActive}>
My Profile
</NavLink>
A user can navigate to users' profiles by navigating to www.sitename./***username*** (like Twitter or Instagram).
<Route path="/:profile_name">
<Profile />
</Route>
When a user is logged in and navigates to their own profile through this dynamic route, I want the nav link for My Profile to be set to active. But I can only figure out how to set My Profile to active when My Profile is clicked on, not when the user navigates to their own profile through the address bar.
How can I set the NavLink
for My Profile to active when a user navigates to their dynamic profile route through the address bar?
Example
In my code example, I have the currentUser
hard coded in as dash123
, so when we navigate to http://somedomain./dash123
the Profile
ponent recognizes /dash123
is the current user's profile and sets the state authorized
to true. When authorized is true
, Profile
renders to display "Edit your profile". Can I also make it so when authorized
is true
, the NavLink
for My Profile is set to active?
CodeSandbox
Code:
import React, { useState, useEffect } from "react";
import {
NavLink,
BrowserRouter as Router,
Route,
Switch,
useParams
} from "react-router-dom";
function Nav() {
const isActive = {
fontWeight: "bold",
backgroundColor: "lightgrey"
};
return (
<ul className="navbar-nav mr-auto">
<li className="nav-item">
<NavLink className="nav-link" to="/Shop" activeStyle={isActive}>
Shop
</NavLink>
</li>
<li className="nav-item">
<NavLink className="nav-link" to="/my_profile" activeStyle={isActive}>
My Profile
</NavLink>
</li>
</ul>
);
}
function Shop(props) {
return (
<div>
Lots of items to buy
<br /> Shoes $4.99
<br /> Carrots $9.99
<br /> Teslas $800,000
</div>
);
}
function Profile(props) {
let { profile_name } = useParams();
const [profile, setProfile] = useState();
const [authorized, setAuthorized] = useState(null);
// fake calls to database
const currentUser = () => {
return { name: "Dashie" };
};
const userInfo = (username) => {
const db = [
{ username: "dash123", name: "Dashie" },
{ username: "bob123", name: "Bob" }
];
return db.find((userDoc) => userDoc.username === username);
};
useEffect(() => {
if (props.profile === "currentUser") {
setProfile(currentUser());
} else {
setProfile(userInfo(profile_name));
}
}, [profile_name, props.profile]);
useEffect(() => {
if (profile && profile.name === currentUser().name) {
setAuthorized(true);
}
}, [profile]);
return (
<div>
Profile:
<br />
{profile && <>Name: {profile.name}</>}
<br />
{authorized && profile && <>Edit your profile, {profile.name}</>}
</div>
);
}
export default function App() {
return (
<div className="App">
<Router>
<Nav />
<Switch>
<Route path="/shop">
<Shop />
</Route>
<Route path="/my_profile">
<Profile profile={"currentUser"} />
</Route>
<Route path="/:profile_name">
<Profile />
</Route>
</Switch>
</Router>
<h2>Problem</h2>
<p>
If you click on <b>Shop</b>, as expected the <b>Shop</b> nav link will
be highlighted.
<br />
<br /> Likewise, the <b>My Profile</b> nav link will be highlighted when
we click on it and navigate to the user's profile.
<br />
<br /> But, we also want to have <b>My Profile</b> highlighted when we
navigate to,{" "}
<i>
"https://whateverdomainyouron/<b>dash123</b>"
</i>{" "}
since this takes us to the current user's profile.
</p>
</div>
);
}
Share
Improve this question
edited Mar 10, 2021 at 17:24
Dashiell Rose Bark-Huss
asked Mar 9, 2021 at 3:27
Dashiell Rose Bark-HussDashiell Rose Bark-Huss
2,9856 gold badges38 silver badges67 bronze badges
0
2 Answers
Reset to default 4Issue
Your auth state resides in your Profile
ponent so it isn't reachable within the Nav
ponent.
Solution
Refactor your code to lift the authentication state above the Nav
ponent and pass it as a prop (or consume in context) and set the active link state accordingly. The following solution uses a React Context.
Create an
AuthContext
and provider ponent.const AuthContext = createContext({ authorized: false, profile: null, setAuthorized: () => {}, setProfile: () => {} }); const AuthProvider = ({ children }) => { const [profile, setProfile] = useState(); const [authorized, setAuthorized] = useState(null); const value = { authorized, profile, setAuthorized, setProfile }; return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>; };
Wrap the Router
with the AuthProvider
<AuthProvider>
<Router>
<Nav />
<Switch>
<Route path="/shop">
<Shop />
</Route>
<Route path="/my_profile">
<Profile profile={"currentUser"} />
</Route>
<Route path="/:profile_name">
<Profile />
</Route>
</Switch>
</Router>
</AuthProvider>
Consume the
AuthContext
inProfile
to set auth state. I.E. swap the olduseState
hooks for theuseContext
hook.function Profile(props) { const { profile_name } = useParams(); const { authorized, profile, setAuthorized, setProfile } = useContext( AuthContext ); ... return ( <div> Profile: <br /> {profile && <>Name: {profile.name}</>} <br /> {authorized && profile && <>Edit your profile, {profile.name}</>} </div> ); }
Consume the
AuthContext
inNav
to ready the auth state and set theNavLink
active state accordingly.function Nav() { const { authorized } = useContext( AuthContext ); const isActive = { fontWeight: "bold", backgroundColor: "lightgrey" }; return ( <ul className="navbar-nav mr-auto"> <li className="nav-item"> <NavLink className="nav-link" to="/Shop" activeStyle={isActive}> Shop </NavLink> </li> <li className="nav-item"> <NavLink isActive={() => authorized} // <-- pass isActive callback className="nav-link" to="/my_profile" activeStyle={isActive} > My Profile </NavLink> </li> </ul> ); }
Demo
Edit
Only I still see a plication. Once we navigate to /dash123 the authorized context is set, and My Profile active- as I want-good. But if we then click "Shop", Shop bees active but My Profile is still active and it shouldn't be. We could fix this by changing the authorized context in the Shop ponent. It seems this could get out of hand because we'd have to access and change the context from every ponent that is linked or redirect ing from Profile. Is there a simpler solution or is this just a plication that's to be expected?
Best I could do for this is to simplify the routes a bit by nesting user profile ids under the "/my_profile" path, and applying a bit more logic on the path matching in the Nav
ponent.
Consolidate the profile routes into a single
Route
that renders for both URLs. Notice here that order of path matching still matters, specifying the more specific path first.<Route path={["/my_profile/:profile_name", "/my_profile"]}> <Profile /> </Route>
Instead of passing a
profile
prop toProfile
, provide it as a default param value. Update theuseEffect
logic to useprofile_name
.const { profile_name = "currentUser" } = useParams(); ... useEffect(() => { if (profile_name === "currentUser") { setProfile(currentUser()); } else { setProfile(userInfo(profile_name)); } }, [profile_name, props.profile]);
Use the
useRouteMatch
hook to match the "/my_profile" path prefix and adjust theisActive
callback logic. The link should be active if an exact match to "/my_profile", or an in-exact match and user authenticated.const match = useRouteMatch("/my_profile"); ... <NavLink isActive={() => match?.isExact || (match && authorized)} className="nav-link" to="/my_profile" activeStyle={isActive} > My Profile </NavLink>
I took Drew Reese's suggestions and expanded on them to fit my needs.
I made it so the My Profile link is active if isProfileRoute
and currentUsersProfile
is met.
<NavLink
isActive={() => isProfileRoute && currentUsersProfile}
className="nav-link"
to="/my_profile"
activeStyle={isActive}
>
currentUsersProfile
is on the AuthContext.
const AuthContext = createContext({
authorized: false,
profile: null,
currentUsersProfile: null,
setAuthorized: () => {},
setProfile: () => {},
setCurrentUsersProfile: () => {},
});
The currentUsersProfile
is set in Profile
to true when the current profile belongs to the current logged in user.:
useEffect(() => {
if (profile?.name === currentUser().name) {
setAuthorized(true);
setCurrentUsersProfile(true);
} else {
setCurrentUsersProfile(false);
}
}, [profile]);
I create an array of all the routes:
const Routes = (
<Switch>
<Route path="/shop">
<Shop />
</Route>
<Route path={"/pro"}>
<Profile />
</Route>
<Route path={["/:profile_name", "/my_profile"]}>
<Profile />
</Route>
</Switch>
);
const array = Routes.props.children.map((child) => child.props.path);
// outputs: ['/shop', '/pro', ["/:profile_name", "/my_profile"]]
In Nav
I use this array to check to see if the current route the user is on is a the profile route (ex: '/dash123', '/somename', or the fixed route '/my_profile') using useMatchRoute
function Nav() {
const { currentUsersProfile } = useContext(AuthContext);
const allNonProfileRoutes = array.slice(0, -1);
let nonProfileRoute = useRouteMatch([...allNonProfileRoutes, { path: "/" }]);
const isProfileRoute = !nonProfileRoute.isExact;
codesandbox
本文标签: javascriptHow to set a NavLink to active conditionally on dynamic linksStack Overflow
版权声明:本文标题:javascript - How to set a NavLink to active conditionally on dynamic links - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744214305a2595563.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论