가이가 문제에 직면하고 있습니다. 2 차원 데이터가 있습니다. 데이터에는 링크가 포함 된 중첩 구조가 있습니다.
const data = [
// First Div Panel
[
{
id: 1,
url: "/services",
title: "Services"
},
{
id: 2,
title: "Products",
children: [
{
id: 3,
url: "/themes-templates",
title: "Themes & Templates"
},
{
id: 4,
url: "/open-source",
title: "Open Source"
},
{
id: 5,
url: "/solutions",
title: "Solutions"
}
]
},
{
id: 6,
url: "/work",
title: "Work",
children: [
{
id: 7,
url: "/methodology",
title: "Methodology",
children: [
{
id: 8,
url: "/agile",
title: "Agile",
children: [
{
id: 9,
url: "/scrum",
title: "Scrum"
}
]
}
]
}
]
},
{
id: 10,
url: "/contact-us",
title: "Contact Us"
}
],
// Second Div Panel which contains children of second list item
[
{
id: 3,
url: "/themes-templates",
title: "Themes & Templates"
},
{
id: 4,
url: "/open-source",
title: "Open Source"
},
{
id: 5,
url: "/solutions",
title: "Solutions"
}
],
// Third Div Panel which contains children of third list item
[
{
id: 7,
url: "/methodology",
title: "Methodology",
children: [
{
id: 8,
url: "/agile",
title: "Agile",
children: [
{
id: 9,
url: "/scrum",
title: "Scrum"
}
]
}
]
}
],
// Fourth Div Panel contains the children of the 3rd sub list item
[
{
id: 8,
url: "/agile",
title: "Agile",
children: [
{
id: 9,
url: "/scrum",
title: "Scrum"
}
]
}
],
// Fourth Div Panel contains the children of the 3rd sub sub list item
[
{
id: 9,
url: "/scrum",
title: "Scrum"
}
]
];
내 임무는 그 2 차원 데이터를 활용 react
하고 푸시 패널과 같은 구조 의 모바일 메뉴를 만드는 것입니다.
그럼에도 불구하고, 나는 이런 식으로 만들려고합니다. 내가 한 일은 모든 하위 배열을 별도의 패널로 취급하는 것 div
입니다. 먼저, 서브 어레이 항목은 기본적으로 보이는 루트 패널에서 고려됩니다. 항목에 children
속성 이있는 경우 next
해당 목록 항목에 동적 버튼이 생성됨을 의미합니다. 이 버튼을 클릭 is-visible
하면 패널에 클래스 가 추가 됩니다. 그러나 문제는 버튼 클릭과 관련된 패널을 어떻게 추적 할 것인가입니다. 내가 가진 상태를 사용하려고 activeId
하고 prevId
하지만 내 색인이 제대로 작동하지 않는 및 올바른 패널을 열 수 없습니다. Chrome 관리자 패널에서 내 솔루션을 검사 할 수 있습니다. 내가 뭘 잘못하고 있는지 말해줘서 고마워?
암호:
// Get a hook function
const {useState} = React;
//#region Data
const data = [
// First Div Panel
[
{
id: 1,
url: "/services",
title: "Services"
},
{
id: 2,
title: "Products",
children: [
{
id: 3,
url: "/themes-templates",
title: "Themes & Templates"
},
{
id: 4,
url: "/open-source",
title: "Open Source"
},
{
id: 5,
url: "/solutions",
title: "Solutions"
}
]
},
{
id: 6,
url: "/work",
title: "Work",
children: [
{
id: 7,
url: "/methodology",
title: "Methodology",
children: [
{
id: 8,
url: "/agile",
title: "Agile",
children: [
{
id: 9,
url: "/scrum",
title: "Scrum"
}
]
}
]
}
]
},
{
id: 10,
url: "/contact-us",
title: "Contact Us"
}
],
// Second Div Panel
[
{
id: 3,
url: "/themes-templates",
title: "Themes & Templates"
},
{
id: 4,
url: "/open-source",
title: "Open Source"
},
{
id: 5,
url: "/solutions",
title: "Solutions"
}
],
// Third Div Panel
[
{
id: 7,
url: "/methodology",
title: "Methodology",
children: [
{
id: 8,
url: "/agile",
title: "Agile",
children: [
{
id: 9,
url: "/scrum",
title: "Scrum"
}
]
}
]
}
],
// Fourth Div Panel
[
{
id: 8,
url: "/agile",
title: "Agile",
children: [
{
id: 9,
url: "/scrum",
title: "Scrum"
}
]
}
],
// Fifth Div Panel
[
{
id: 9,
url: "/scrum",
title: "Scrum"
}
]
];
//#endregion Data
//#region Component
const PanelMenu = props => {
const { title } = props;
const [items, setItems] = useState(data);
// Title Header of the Panel
const [headerTitle, setHeaderTitle] = useState(title ? title : "");
// Previous Title Header of the Panel
const [prevHeaderTitle, setPrevHeaderTitle] = useState(title ? title : "");
// ActiveIndex => 0 means by default master-panel is active
const [activeId, setActiveId] = useState(0);
// PreviousIndex
const [prevId, setPrevId] = useState(0);
const handlePanelBtn = (newTitle, index, prevIndex) => {
// Title Checking
const titleProp = title ? title : "";
const prevTitle = index === 0 ? titleProp : headerTitle;
// SetStates
setPrevHeaderTitle(prevTitle);
setHeaderTitle(newTitle);
setActiveId(index);
setPrevId(prevIndex);
};
const panelRenderer = () => {
const panelsJSX = [];
for (let i = 0; i < items.length; i++) {
let childItemIndex = i;
const panels = (
<div
key={i}
id={i === 0 ? "p__master" : `p__student-${i}`}
className={
childItemIndex === activeId
? "p__panel is-visible"
: "p__panel is-hide"
}
>
<ul>
{items[i].map((item, index) => {
// It means it have children
if (item.children && item.children.length > 0) {
childItemIndex++;
return (
<li key={item.id} className="p-next">
{item.url ? (
<a href={item.url} className="p-link">
{item.title}
</a>
) : (
<div className="p-link">{item.title}</div>
)}
<button
type="button"
className="p-next__btn"
data-id={`#p__student-${childItemIndex}`}
onClick={() => handlePanelBtn(item.title, index, prevId)}
>
<span>></span>
</button>
</li>
);
} else {
return (
<li key={item.id}>
<a href={item.url} className="p-link">
{item.title}
</a>
</li>
);
}
})}
</ul>
</div>
);
panelsJSX.push(panels);
}
return panelsJSX;
};
const renderer = () => {
if (items && items.length > 0) {
return (
<div className="p">
<div className="p__wrap">
{/* Panel Actions => Header */}
<div className="p__actions">
{/* Previous Button */}
{activeId !== 0 && (
<button
type="button"
className="p-action__btn left"
onClick={() =>
handlePanelBtn(prevHeaderTitle, prevId, prevId)
}
>
<span><</span>
</button>
)}
{/* Title */}
{headerTitle && (
<div className="p-action__title">{headerTitle}</div>
)}
{/* Close Button */}
<button type="button" className="p-action__btn right">
<span>×</span>
</button>
</div>
{/* Panel children Wrapper */}
<div className="p__children">{panelRenderer()}</div>
</div>
</div>
);
}
};
return <React.Fragment>{renderer()}</React.Fragment>;
};
//#endregion Component
// Render it
ReactDOM.render(
<PanelMenu title="Menu" />,
document.getElementById("root")
)
<style>
*,:before,:after {
box-sizing: border-box;
}
.p__wrap {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 320px;
background-color: #fff;
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
z-index: 1;
color: #333;
overflow-x: hidden;
}
.p__actions {
position: relative;
padding: 14px;
min-height: 54px;
border-bottom: 1px solid #dcdcdc;
}
.p-action__title {
text-align: center;
color: #333;
text-transform: uppercase;
font-weight: 700;
}
.p-action__btn {
position: absolute;
width: 54px;
height: 54px;
top: 0;
right: 0;
font-size: 16px;
color: #333;
border: none;
cursor: pointer;
}
.left {
left: 0;
}
.right {
right: 0;
}
.p__children {
position: relative;
background-color: #fff;
overflow: hidden;
height: calc(100% - 54px);
}
.p__panel {
overflow-x: hidden;
overflow-y: auto;
position: absolute;
transform: translateX(100%);
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 0;
transition: transform 0.2s ease 0s;
}
.p__panel.is-visible {
transform: translateX(0);
z-index: 1;
}
.p__panel.is-hide {
opacity: 0;
visibility: hidden;
}
.p__panel > ul {
margin: 0;
padding: 0;
}
.p__panel > ul > li {
list-style: none;
border-bottom: 1px solid #dcdcdc;
}
.p__panel > ul > li > .p-link {
color: #333;
display: block;
line-height: 22px;
font-size: 14px;
padding: 14px 24px;
background-color: transparent;
cursor: pointer;
}
.p__panel > ul > li > .p-link:hover {
background-color: #dcdcdc;
}
.p-next {
position: relative;
}
.p-next__btn {
position: absolute;
padding: 14px 16px;
font-size: 16px;
line-height: 22px;
top: 0;
right: 0;
background-color: rgb(240,240,240);
color: #333;
border: none;
border-left: 1px solid #dcdcdc;
cursor: pointer;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
답변
다음 코드 샌드 박스에서 코드를 실제 예제로 변환했습니다.
https://codesandbox.io/s/panel-menu-hfrmx?fontsize=14&hidenavigation=1&theme=dark
처음에는 많은 변화가있는 것처럼 보일 수 있으므로 조금 더 자세히 설명하겠습니다.
- 메뉴 헤더와 메뉴 항목 목록을 자체 구성 요소로 추출하여 쉽게 재사용 할 수 있습니다.
- 데이터 구조를 다시 작성 했으므로 메뉴 항목을 두 번 또는 세 번 정의 할 필요가 없습니다. 이 구조는 평평하므로 원하는 데이터베이스에 쉽게 저장할 수 있습니다.
답변
난 당신이 필요하다고 생각
handlePanelBtn(item.title, childItemIndex, prevId)
대신에
handlePanelBtn(item.title, index, prevId)
답변
컨텍스트 API 로이 작업을 수행하여 패널을 표시하는 논리를 단순화했습니다.
현재 패널을 표시하고 메뉴로 돌아가는 데 사용할 수있는 패널 배열이있는 PanelContext라는 컨텍스트를 만듭니다.
import React from "react";
export const PanelContext = React.createContext();
export function PanelProvider({ children }) {
const [currentPanel, setCurrentPanel] = React.useState([0]);
const addItemToPanel = item => setCurrentPanel(prev => [item, ...prev]);
const goBack = () => setCurrentPanel(prev => prev.slice(1));
return (
<PanelContext.Provider
value={{
currentPanel: currentPanel[0],
setCurrentPanel: addItemToPanel,
goBack
}}
>
{children}
</PanelContext.Provider>
);
}
컨텍스트 값에 따라 활성화 된 모든 패널 및 표시 패널을 반복적으로 작성하는 패널 구성 요소를 작성했습니다.
const Panel = ({ items, id, title }) => {
const { currentPanel, setCurrentPanel, goBack } = React.useContext(
PanelContext
);
const panels = [];
return (
<>
<div
className={id === currentPanel ? "p__wrap visible" : " p__wrap hidden"}
>
<h2>
{title && <button onClick={goBack}>{"<"}</button>} {title || "Menu"}{" "}
</h2>
<div className="p__panel">
<ul>
{items.map(item => {
if (item.children)
panels.push(
<Panel
title={item.title}
id={item.id}
items={item.children}
/>
);
return (
<React.Fragment key={item.id}>
<li>
{item.title}
{item.children && (
<button
onClick={() => {
setCurrentPanel(item.id);
}}
>
{">"}
</button>
)}
</li>
</React.Fragment>
);
})}
</ul>
</div>
</div>
{panels}
</>
);
};
export const PanelMenu = props => {
return (
<PanelProvider>
<Panel items={data} id={0} />
</PanelProvider>
);
};
나는 당신의 CSS를 깨뜨 렸습니다.
자식이 깊게 중첩 된 하나의 개체 만 사용했습니다.
작동하는 코드 샌드 박스는 다음과 같습니다. https://codesandbox.io/s/panel-menu-c871j