根据浏览器宽度和/或用户点击反应切换响应侧边栏
React toggle responsive sidebar based on browser width &/or user clicks
在这里反应菜鸟。我正在创建一个包含三个部分的仪表板。边栏 |导航栏和导航栏下方的内容。
侧边栏行为应该是:最初打开;当浏览器宽度 < 498px 时自动隐藏;当浏览器宽度增加超过 498px 时自动打开; open/close 当点击导航栏切换时,无论浏览器 window 宽度如何(即移动设备或桌面设备)。
我知道如何在 jQuery 中执行此操作,但想学习 React 方式。搜索论坛和 Google,我创建了一个工作模型。但是,我觉得代码可以简化或至少可以针对问题进行审计。希望前辈指教,欢迎大家帮忙。担心依赖警告和调用 2 个处理程序。没有使用 addEventListener,因为我读到 Safari 可能有问题。
Notes,我避免使用 css 媒体查询来自动隐藏,因为我不知道如何覆盖 display:none
的 css 文件媒体查询使用点击组件时来自 React。使用反应 17.0.2; bootstrap5.1.3;
Dashboard.js
import { useEffect, useRef, useState } from 'react';
import "./Dashboard.css";
import Navbar from "./Navbar";
import Sidebar from "./Sidebar";
const Dashboard = () => {
const sidebarRef = useRef(null); // used to get sidebar width
const [usMobile, setMobile] = useState("");
const mq = window.matchmedia("(max-width: 498px)");
const [firsTime, setFirsTime] = useState(true);
useEffect(() => {
//handle sidebar display clicks from Navbar
// makes/sets initial sidebar state to open
// returns true when window is < 498px
// unmount cleanup handler
toggleSidebar();
mq.addListener(toggleSidebar);
return () => mq.removeListener(toggleSidebar);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); // warning about dependency here
useEffect(() => {
// handles sidebar display based on resize
// returns treu when window is < 498
mq.addListener(hideSideBar);
return () => mq.removeListener(hideSideBar);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); // warning about dependency here
const hideSideBar = () => {
if(mq.matches) {
setMobile(true);
} else {
setMobile(false);
}
}
// toggle sidebar based on user clicks
const toggleSidebar = ( clicked ) => {
let sidebarWidth = sidebarRef.current.offsetWidth;
if (firsTime) {
setShowSidebar(true);
setMobile(false);
setFirstTime(false);
} else {
if ( clicked = "yes") {
if (sidebarWidth == 0){
setShowSidebar(true);
setMobile(false);
} else {
setShowSidebar(false);
}
clicked = "";
}
}
}
return (
<span ref={sidebarRef}>
{!isMobile && showSidebar && <Sidebar onClick={toggleSidebar} />}
</span>
<div className="flex-fill content-wrapper">
<Navbar showSideBar={showSidebar} onClick={toggleSidebar} />
</div>
);
}
export default Dashboard;
Navbar.js
import {useState} from 'react';
const Navbar = (props) => {
// eslint-disable-next-line no-unused-vars
// set is unused but it will not work otherwise
const [clicked, setClicked] = useState("yes");
return (
<div className="d-flex justify-content-between bg-white py-2 ps-3 pe-4">
<div className="col-auto">
<button
className="menu-icon-btn"
onClick={() => props.onClick(clicked)} >
</button>
</div>
</div>
)
}
export default Navbar;
Sidebar.js
import {useState} from 'react';
const Sidebar = (props) => {
const [clicked, setClicked] = useState('yes');
return (
<div className="d-flex flex-column flex-shrink-0 text-white bg-dark sidebar">
<span className="fs4">Sidebar Title</span>
<ul className="nav nav-pills flex-column mb-auto">
<li className="nav-item">
Home
</li>
</ul>
<hr>
<span id="toggle-x"
className="btn btn-outline-primary border-0 inline d-md-none mx-auto mb-2">
aria-label="Toggle Sidebar Nav"
onClick={() => props.onClick(clicked)} >
CLOSE
</span>
</div>
)
}
export default Sidebar;
Dashboard.css
大多数 css 来自 bootstrap;这些只是应用自定义样式
.menu-icon-btn {
background: none;
border: none;
padding: 0;
}
.toggle-icon {
width: 32px;
height: 32px;
fill: var(--medium-gray);
cursor: pointer;
}
.menu-icon {
width: 20px;
height: 20px;
fill: var(--medium-gray);
cursor: pointer;
}
.sidebar,
.content-wrapper {
height: 100vh;
}
.sidebar-wrapper {
width: 200px;
}
/* Omitted the following as I was unable to force it to change from within React. In jQuery(show/hide seem to override this just fine.)*/
/* @media screen and (min-width: 0px) and (max-width: 700px) {
.sidebar-wrapper {
display: none;
}
} */
New Dashboard.js
const Dashboard = () => {
const isDesktop = () => window.innerWidth > 598;
const [sidebarStatus, setSidebarStatus] = useState("");
useEffect(() => {
window.addEventListener("resize", () => {
setSidebarStatus(isDesktop());
});
return () => window.removeEventListener("resize", isDesktop);
}, []);
const toggleSidebar = (open) => {
setSidebarStatus(open);
};
return (
{sidebarStatus && (
<Sidebar showSideBar={sidebarStatus} onClick={toggleSidebar} />
)}
)
}
New Navbar.js
const Navbar = (props) => {
return (
<button
id="toggle"
className="menu-icon-btn py-2"
data-menu-icon-btn
onClick={() => props.onClick(!props.showSideBar)}
> Toggle Sidebar
</button>
)
}
您的代码已损坏..我对您的代码做了一点改动:
import React, { useEffect, useRef, useState } from "react";
import "./Dashboard.css";
import Navbar from "./Navbar";
import Sidebar from "./Sidebar";
const Dashboard = () => {
// i don't know what is using for..
const sidebarRef = useRef(null); // used to get sidebar width
const [isMobile, setMobile] = useState(document.body.clientWidth <= 498);
// use 'init' | 'open' | 'close', that you don't need remember if suer clicked
const [sidebarStatus, setSidebarStatus] = useState("init");
useEffect(() => {
// add listener only once, or many listeners would be created every render
const mq = window.matchMedia("(max-width: 498px)");
mq.addListener((res) => {
setMobile(res.matches);
});
return () => mq.removeListener(toggleSidebar);
}, []);
// react use status change fire effects, fire function manually is tired
// here calculate should show sidebar
const showSidebar =
sidebarStatus === "open" || (!isMobile && sidebarStatus === "init");
const toggleSidebar = (open) => {
setSidebarStatus(open ? "open" : "close");
};
return (
<>
<span ref={sidebarRef}>
{showSidebar && <Sidebar onClick={toggleSidebar} />}
</span>
<div className="flex-fill content-wrapper">
<Navbar showSideBar={showSidebar} onClick={toggleSidebar} />
</div>
</>
);
};
export default Dashboard;
注意导航栏和侧边栏的 toggleSidebar 参数是常量 true
和 false
。 不要在不同的文件中使用状态
在这里反应菜鸟。我正在创建一个包含三个部分的仪表板。边栏 |导航栏和导航栏下方的内容。
侧边栏行为应该是:最初打开;当浏览器宽度 < 498px 时自动隐藏;当浏览器宽度增加超过 498px 时自动打开; open/close 当点击导航栏切换时,无论浏览器 window 宽度如何(即移动设备或桌面设备)。
我知道如何在 jQuery 中执行此操作,但想学习 React 方式。搜索论坛和 Google,我创建了一个工作模型。但是,我觉得代码可以简化或至少可以针对问题进行审计。希望前辈指教,欢迎大家帮忙。担心依赖警告和调用 2 个处理程序。没有使用 addEventListener,因为我读到 Safari 可能有问题。
Notes,我避免使用 css 媒体查询来自动隐藏,因为我不知道如何覆盖 display:none
的 css 文件媒体查询使用点击组件时来自 React。使用反应 17.0.2; bootstrap5.1.3;
Dashboard.js
import { useEffect, useRef, useState } from 'react';
import "./Dashboard.css";
import Navbar from "./Navbar";
import Sidebar from "./Sidebar";
const Dashboard = () => {
const sidebarRef = useRef(null); // used to get sidebar width
const [usMobile, setMobile] = useState("");
const mq = window.matchmedia("(max-width: 498px)");
const [firsTime, setFirsTime] = useState(true);
useEffect(() => {
//handle sidebar display clicks from Navbar
// makes/sets initial sidebar state to open
// returns true when window is < 498px
// unmount cleanup handler
toggleSidebar();
mq.addListener(toggleSidebar);
return () => mq.removeListener(toggleSidebar);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); // warning about dependency here
useEffect(() => {
// handles sidebar display based on resize
// returns treu when window is < 498
mq.addListener(hideSideBar);
return () => mq.removeListener(hideSideBar);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); // warning about dependency here
const hideSideBar = () => {
if(mq.matches) {
setMobile(true);
} else {
setMobile(false);
}
}
// toggle sidebar based on user clicks
const toggleSidebar = ( clicked ) => {
let sidebarWidth = sidebarRef.current.offsetWidth;
if (firsTime) {
setShowSidebar(true);
setMobile(false);
setFirstTime(false);
} else {
if ( clicked = "yes") {
if (sidebarWidth == 0){
setShowSidebar(true);
setMobile(false);
} else {
setShowSidebar(false);
}
clicked = "";
}
}
}
return (
<span ref={sidebarRef}>
{!isMobile && showSidebar && <Sidebar onClick={toggleSidebar} />}
</span>
<div className="flex-fill content-wrapper">
<Navbar showSideBar={showSidebar} onClick={toggleSidebar} />
</div>
);
}
export default Dashboard;
Navbar.js
import {useState} from 'react';
const Navbar = (props) => {
// eslint-disable-next-line no-unused-vars
// set is unused but it will not work otherwise
const [clicked, setClicked] = useState("yes");
return (
<div className="d-flex justify-content-between bg-white py-2 ps-3 pe-4">
<div className="col-auto">
<button
className="menu-icon-btn"
onClick={() => props.onClick(clicked)} >
</button>
</div>
</div>
)
}
export default Navbar;
Sidebar.js
import {useState} from 'react';
const Sidebar = (props) => {
const [clicked, setClicked] = useState('yes');
return (
<div className="d-flex flex-column flex-shrink-0 text-white bg-dark sidebar">
<span className="fs4">Sidebar Title</span>
<ul className="nav nav-pills flex-column mb-auto">
<li className="nav-item">
Home
</li>
</ul>
<hr>
<span id="toggle-x"
className="btn btn-outline-primary border-0 inline d-md-none mx-auto mb-2">
aria-label="Toggle Sidebar Nav"
onClick={() => props.onClick(clicked)} >
CLOSE
</span>
</div>
)
}
export default Sidebar;
Dashboard.css
大多数 css 来自 bootstrap;这些只是应用自定义样式
.menu-icon-btn {
background: none;
border: none;
padding: 0;
}
.toggle-icon {
width: 32px;
height: 32px;
fill: var(--medium-gray);
cursor: pointer;
}
.menu-icon {
width: 20px;
height: 20px;
fill: var(--medium-gray);
cursor: pointer;
}
.sidebar,
.content-wrapper {
height: 100vh;
}
.sidebar-wrapper {
width: 200px;
}
/* Omitted the following as I was unable to force it to change from within React. In jQuery(show/hide seem to override this just fine.)*/
/* @media screen and (min-width: 0px) and (max-width: 700px) {
.sidebar-wrapper {
display: none;
}
} */
New Dashboard.js
const Dashboard = () => {
const isDesktop = () => window.innerWidth > 598;
const [sidebarStatus, setSidebarStatus] = useState("");
useEffect(() => {
window.addEventListener("resize", () => {
setSidebarStatus(isDesktop());
});
return () => window.removeEventListener("resize", isDesktop);
}, []);
const toggleSidebar = (open) => {
setSidebarStatus(open);
};
return (
{sidebarStatus && (
<Sidebar showSideBar={sidebarStatus} onClick={toggleSidebar} />
)}
)
}
New Navbar.js
const Navbar = (props) => {
return (
<button
id="toggle"
className="menu-icon-btn py-2"
data-menu-icon-btn
onClick={() => props.onClick(!props.showSideBar)}
> Toggle Sidebar
</button>
)
}
您的代码已损坏..我对您的代码做了一点改动:
import React, { useEffect, useRef, useState } from "react";
import "./Dashboard.css";
import Navbar from "./Navbar";
import Sidebar from "./Sidebar";
const Dashboard = () => {
// i don't know what is using for..
const sidebarRef = useRef(null); // used to get sidebar width
const [isMobile, setMobile] = useState(document.body.clientWidth <= 498);
// use 'init' | 'open' | 'close', that you don't need remember if suer clicked
const [sidebarStatus, setSidebarStatus] = useState("init");
useEffect(() => {
// add listener only once, or many listeners would be created every render
const mq = window.matchMedia("(max-width: 498px)");
mq.addListener((res) => {
setMobile(res.matches);
});
return () => mq.removeListener(toggleSidebar);
}, []);
// react use status change fire effects, fire function manually is tired
// here calculate should show sidebar
const showSidebar =
sidebarStatus === "open" || (!isMobile && sidebarStatus === "init");
const toggleSidebar = (open) => {
setSidebarStatus(open ? "open" : "close");
};
return (
<>
<span ref={sidebarRef}>
{showSidebar && <Sidebar onClick={toggleSidebar} />}
</span>
<div className="flex-fill content-wrapper">
<Navbar showSideBar={showSidebar} onClick={toggleSidebar} />
</div>
</>
);
};
export default Dashboard;
注意导航栏和侧边栏的 toggleSidebar 参数是常量 true
和 false
。 不要在不同的文件中使用状态