如何在react-router-dom v6中设置激活链接样式(Chakra UI)
实现React Router v6激活链接保持Chakra UI悬停样式
核心思路
借助React Router v6中NavLink的激活状态判断能力,将Chakra UI定义的悬停样式同步应用到激活状态的导航链接上,替代手动通过location判断的冗余逻辑。
具体修改步骤
1. 顶部导航项激活样式适配
在SidebarContent组件中,通过NavLink的内置激活状态,将isActive属性传递给NavItem组件,让导航项根据激活状态自动切换样式:
// SidebarContent中修改NavLink部分 {link.name === "Financial data" || link.name === "More" ? ( <NavItem icon={link.icon} hasDropdown={link.hasDropdown}> {link.name} </NavItem> ) : ( <div className="nav"> <NavLink to={`/dashboard/${link.name.toLowerCase().replace(" ", "-")}`} children={({ isActive }) => ( <NavItem icon={link.icon} hasDropdown={link.hasDropdown} isActive={isActive}> {link.name} </NavItem> )} /> </div> )}
更新NavItem组件,接收isActive属性并同步激活样式:
function NavItem({ icon, children, hasDropdown, isActive = false, ...rest }) { const [isDropdownOpen, setIsDropdownOpen] = useState(false); const handleDropdownToggle = () => { setIsDropdownOpen(!isDropdownOpen); }; return ( <Box as="div" style={{ textDecoration: "none" }} _focus={{ boxShadow: "none" }} mt={4} > <Flex align="center" p="2" mx="2" borderRadius="lg" role="group" fontSize="15px" cursor="pointer" _hover={{ bg: "#002c8a", color: "white", borderColor: "white" }} // 激活状态复用悬停样式 bg={isActive ? "#002c8a" : "transparent"} color={isActive ? "white" : "inherit"} onClick={hasDropdown ? handleDropdownToggle : undefined} {...rest} > {icon && ( <Icon mr="4" p={1} border="1px" borderRadius="full" borderColor={isActive ? "white" : "gray.400"} fontSize="24" as={icon} /> )} {children} </Flex> {hasDropdown && isDropdownOpen && <DropdownOptions name={children} />} </Box> ); }
2. 下拉菜单选项激活样式适配
在DropdownOptions组件中,移除手动通过location判断的逻辑,直接利用NavLink的isActive属性设置激活样式:
function DropdownOptions({ name }) { const options = getDropdownOptions(name); return ( <Flex flexDirection="column" ml="4" boxShadow="lg" w="150px" p={2} borderRadius="lg" > {options.map((option) => ( <NavLink key={option.text} to={`/dashboard/${option.text.toLowerCase()}`} children={({ isActive }) => ( <Box as="div" py="2" px="1" borderRadius="lg" w="130px" fontSize="14px" _hover={{ bg: "#002c8a", color: "white" }} // 激活状态复用悬停样式 bg={isActive ? "#002c8a" : "transparent"} color={isActive ? "white" : "inherit"} > {option.icon && ( <Icon mr="2" fontSize="14" verticalAlign="middle" as={option.icon} /> )} {option.text} </Box> )} /> ))} </Flex> ); }
完整修改后的代码
import React, { useState } from "react"; import { Box, CloseButton, Flex, Icon, useColorModeValue, Drawer, DrawerContent, useDisclosure } from "@chakra-ui/react"; import { FiCompass, FiSettings } from "react-icons/fi"; import { NavLink } from "react-router-dom"; import { AiFillClockCircle, AiOutlineTransaction, AiOutlineDashboard } from "react-icons/ai"; import { IoApps, IoLockOpen, IoAnalytics } from "react-icons/io5"; import { BsFillPeopleFill, BsFillPersonFill, BsBookHalf, BsCurrencyBitcoin } from "react-icons/bs"; const LinkItems = [ { name: "Overview", icon: AiOutlineDashboard }, { name: "Apps", icon: IoApps }, { name: "Customers", icon: BsFillPeopleFill }, { name: "Financial data", icon: IoAnalytics, hasDropdown: true }, { name: "More", icon: FiSettings, hasDropdown: true } ]; function SideNav() { const token = sessionStorage.getItem("token"); const { isOpen, onOpen, onClose } = useDisclosure(); return ( <Box bg={useColorModeValue("gray.100", "gray.900")}> <SidebarContent onClose={() => onClose} display={{ base: "none", md: "block" }} /> <Drawer isOpen={isOpen} placement="left" onClose={onClose} returnFocusOnClose={false} onOverlayClick={onClose} size="full" > <DrawerContent> <SidebarContent onClose={onClose} /> </DrawerContent> </Drawer> <Box ml={{ base: 0, md: 60 }} p={{ base: 0, md: "4" }}> {/* Content */} </Box> </Box> ); } function SidebarContent({ onClose, ...rest }) { return ( <Box bg="whiteAlpha.800" borderRight="1px" borderRightColor={useColorModeValue("gray.200", "gray.700")} w={{ base: "full", md: 48, lg: 44, xl: 48, "2xl": 60 }} pos="fixed" overflow="auto" sx={{ "&::-webkit-scrollbar": { width: "6px", backgroundColor: "#f1f1f1", borderRadius: "68px" }, "&::-webkit-scrollbar-thumb": { backgroundColor: "#c1c1c1", borderRadius: "68px" } }} h="full" {...rest} > <Flex h="20" mb={12} alignItems="center" mx="8" justifyContent="space-between" > <CloseButton display={{ base: "flex", md: "none" }} onClick={onClose} /> </Flex> {LinkItems.map((link) => ( <React.Fragment key={link.name}> {link.name === "Financial data" || link.name === "More" ? ( <NavItem icon={link.icon} hasDropdown={link.hasDropdown}> {link.name} </NavItem> ) : ( <div className="nav"> <NavLink to={`/dashboard/${link.name.toLowerCase().replace(" ", "-")}`} children={({ isActive }) => ( <NavItem icon={link.icon} hasDropdown={link.hasDropdown} isActive={isActive}> {link.name} </NavItem> )} /> </div> )} </React.Fragment> ))} </Box> ); } function NavItem({ icon, children, hasDropdown, isActive = false, ...rest }) { const [isDropdownOpen, setIsDropdownOpen] = useState(false); const handleDropdownToggle = () => { setIsDropdownOpen(!isDropdownOpen); }; return ( <Box as="div" style={{ textDecoration: "none" }} _focus={{ boxShadow: "none" }} mt={4} > <Flex align="center" p="2" mx="2" borderRadius="lg" role="group" fontSize="15px" cursor="pointer" _hover={{ bg: "#002c8a", color: "white", borderColor: "white" }} bg={isActive ? "#002c8a" : "transparent"} color={isActive ? "white" : "inherit"} onClick={hasDropdown ? handleDropdownToggle : undefined} {...rest} > {icon && ( <Icon mr="4" p={1} border="1px" borderRadius="full" borderColor={isActive ? "white" : "gray.400"} fontSize="24" as={icon} /> )} {children} </Flex> {hasDropdown && isDropdownOpen && <DropdownOptions name={children} />} </Box> ); } function DropdownOptions({ name }) { const options = getDropdownOptions(name); return ( <Flex flexDirection="column" ml="4" boxShadow="lg" w="150px" p={2} borderRadius="lg" > {options.map((option) => ( <NavLink key={option.text} to={`/dashboard/${option.text.toLowerCase()}`} children={({ isActive }) => ( <Box as="div" py="2" px="1" borderRadius="lg" w="130px" fontSize="14px" _hover={{ bg: "#002c8a", color: "white" }} bg={isActive ? "#002c8a" : "transparent"} color={isActive ? "white" : "inherit"} > {option.icon && ( <Icon mr="2" fontSize="14" verticalAlign="middle" as={option.icon} /> )} {option.text} </Box> )} /> ))} </Flex> ); } function getDropdownOptions(name) { if (name === "Financial data") { return [ { text: "Records", icon: AiFillClockCircle }, { text: "Transactions", icon: AiOutlineTransaction }, { text: "Balance", icon: BsBookHalf }, { text: "Auth", icon: IoLockOpen } ]; } else if (name === "More") { return [ { text: "Logs", icon: IoAnalytics }, { text: "Billings", icon: BsCurrencyBitcoin }, { text: "Teams", icon: FiCompass }, { text: "Profile", icon: BsFillPersonFill } ]; } return []; } export default SideNav;
内容的提问来源于stack exchange,提问作者Noob
相关产品推荐
相关产品推荐

