[reactjs] 메뉴가 올바른 인덱스 div를 열지 않습니다

가이가 문제에 직면하고 있습니다. 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>&gt;</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>&lt;</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)

https://codesandbox.io/s/panel-menu-2uwxo


답변

컨텍스트 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


답변