Mở menu đóng mở theo mặc định dựa trên id


8

Tôi đang tạo một menu và các menu con lồng nhau và mọi thứ đã được thực hiện ngay bây giờ .. Bây giờ tôi cần phải tạo menu thu gọn này để được mở theo mặc định dựa trên id đã cho ..

Bạn cũng có thể xem đoạn mã làm việc hoàn chỉnh bên dưới,

const loadMenu = () => Promise.resolve([{id:"1",name:"One",children:[{id:"1.1",name:"One - one",children:[{id:"1.1.1",name:"One - one - one"},{id:"1.1.2",name:"One - one - two"},{id:"1.1.3",name:"One - one - three"}]}]},{id:"2",name:"Two",children:[{id:"2.1",name:"Two - one"}]},{id:"3",name:"Three",children:[{id:"3.1",name:"Three - one",children:[{id:"3.1.1",name:"Three - one - one",children:[{id:"3.1.1.1",name:"Three - one - one - one",children:[{id:"3.1.1.1.1",name:"Three - one - one - one - one"}]}]}]}]},{id:"4",name:"Four"},{id:"5",name:"Five",children:[{id:"5.1",name:"Five - one"},{id:"5.2",name:"Five - two"},{id:"5.3",name:"Five - three"},{id:"5.4",name:"Five - four"}]},{id:"6",name:"Six"}]);

const openMenuId = "3.1.1.1";

const {Component, Fragment} = React;
const {Button, Collapse, Input} = Reactstrap;

class Menu extends Component {
  constructor(props) {
    super(props);
    this.state = {menuItems: []};
  }

  render() {
    return <MenuItemContainer menuItems={this.state.menuItems} />;
  }

  componentDidMount() {
    loadMenu().then(menuItems => this.setState({menuItems}));
  }
}

function MenuItemContainer(props) {
  if (!props.menuItems.length) return null;
  
  const renderMenuItem = menuItem =>
    <li key={menuItem.id}><MenuItem {...menuItem} /></li>;
    
  return <ul>{props.menuItems.map(renderMenuItem)}</ul>;
}
MenuItemContainer.defaultProps = {menuItems: []};

class MenuItem extends Component {
  constructor(props) {
    super(props);
    this.state = {isOpen: false};
    this.toggle = this.toggle.bind(this);
  }

  render() {
    let isLastChild = this.props.children ? false : true;
    return (
      <Fragment>
        <Button onClick={this.toggle}>{this.props.name}</Button>
        <Fragment>
          {isLastChild ? <Input type="checkbox" value={this.props.id} /> : ''}
        </Fragment>
        <Collapse isOpen={this.state.isOpen}>
          <MenuItemContainer menuItems={this.props.children} />
        </Collapse>
      </Fragment>
    );
  }

  toggle() {
    this.setState(({isOpen}) => ({isOpen: !isOpen}));
  }
}

ReactDOM.render(<Menu />, document.getElementById("root"));
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reactstrap/8.4.1/reactstrap.min.js"></script>

<div id="root"></div>

Yêu cầu:

Tôi đang có một giá trị id được lưu trữ trong const openMenuId = "3.1.1.1.1"; thành phần cha mẹ (bạn có thể xem biến này bên dưới loadMenubiến mảng) ..

Mặc dù có nhiều menu con, id này sẽ chỉ thuộc về id con cấp độ cuối và do đó sẽ có một hộp kiểm chắc chắn để hộp kiểm cần phải được kiểm tra và các menu lên đến cấp cha mẹ cần được mở.

Ví dụ..,

Khi openMenuId là "3.1.1.1.1"và do đó nó là rõ ràng rằng mức độ con cuối cùng của menu threeđó là Three - one - one - one - onenhu cầu được kiểm tra nhưopenMenuId và giá trị hộp kiểm có một trận đấu ở đây .. Sau đó, các menu và menu con tương ứng cần phải được mở rộng lên đến mức cuối cùng.

Điều này chỉ dành cho hành vi mặc định trên trang được truy cập để sau đó người dùng có thể thu gọn lại và có thể kiểm tra bất kỳ hộp kiểm nào khác trong bất kỳ menu nào khác .. Nhưng trong khi truy cập trang, tôi sẽ có một id cụ thể cần được mở theo mặc định và cả cần phải được kiểm tra trong hộp kiểm tra ..

Vui lòng giúp tôi đạt được kết quả của việc mở menu tương ứng bằng cách so sánh id được truyền lại dưới dạng đạo cụ và làm cho menu tương ứng được kiểm tra ..

Đấu tranh trong một thời gian dài, vì vậy xin vui lòng giúp tôi .. Một lời cảm ơn lớn trước ..

Câu trả lời:


3

Thật là một câu hỏi tuyệt vời! Tôi thực sự rất thích đến với một giải pháp cho cái này.

Vì bạn muốn đưa ra trạng thái ban đầu cho cả trạng thái menu và trạng thái hộp kiểm, tôi nghĩ rằng việc kiểm soát trạng thái của cả hai ở <Menu>cấp độ (hoặc thậm chí cao hơn!) Là một ý tưởng tốt. Điều này không chỉ giúp bạn dễ dàng xác định trạng thái ban đầu từ cha mẹ mà còn cho phép bạn linh hoạt hơn nếu bạn cần một số hành vi hoặc hộp kiểm phức tạp hơn trong tương lai.

Vì cấu trúc của các menu là đệ quy, tôi nghĩ rằng có một cấu trúc đệ quy cho trạng thái menu hoạt động khá tốt. Trước khi tôi đi vào mã, đây là một GIF ngắn, tôi hy vọng, sẽ giúp giải thích trạng thái trông như thế nào:

Bản giới thiệu

Video hiển thị menu ba cột: menu, trạng thái menu dưới dạng JSON và trạng thái hộp kiểm là JSON.  Khi menu và hộp kiểm được nhấp, trạng thái cập nhật.

Đây là đoạn trích sân chơi:

Câu trả lời

Mã hướng dẫn dưới đây.

const loadMenu = () =>
  Promise.resolve([
    {
      id: "1",
      name: "One",
      children: [
        {
          id: "1.1",
          name: "One - one",
          children: [
            { id: "1.1.1", name: "One - one - one" },
            { id: "1.1.2", name: "One - one - two" },
            { id: "1.1.3", name: "One - one - three" }
          ]
        }
      ]
    },
    { id: "2", name: "Two", children: [{ id: "2.1", name: "Two - one" }] },
    {
      id: "3",
      name: "Three",
      children: [
        {
          id: "3.1",
          name: "Three - one",
          children: [
            {
              id: "3.1.1",
              name: "Three - one - one",
              children: [
                {
                  id: "3.1.1.1",
                  name: "Three - one - one - one",
                  children: [
                    { id: "3.1.1.1.1", name: "Three - one - one - one - one" }
                  ]
                }
              ]
            }
          ]
        }
      ]
    },
    { id: "4", name: "Four" },
    {
      id: "5",
      name: "Five",
      children: [
        { id: "5.1", name: "Five - one" },
        { id: "5.2", name: "Five - two" },
        { id: "5.3", name: "Five - three" },
        { id: "5.4", name: "Five - four" }
      ]
    },
    { id: "6", name: "Six" }
  ]);

const { Component, Fragment } = React;
const { Button, Collapse, Input } = Reactstrap;

const replaceNode = (replacer, node, idPath, i) => {
  if (i <= idPath.length && !node) {
    // Not at target node yet, create nodes in between
    node = {};
  }
  if (i > idPath.length) {
    // Reached target node
    return replacer(node);
  }

  // Construct ID that matches this depth - depth meaning
  // the amount of dots in between the ID
  const id = idPath.slice(0, i).join(".");
  return {
    ...node,
    // Recurse
    [id]: replaceNode(replacer, node[id], idPath, i + 1)
  };
};

const replaceNodeById = (node, id, visitor) => {
  // Pass array of the id's parts instead of working on the string
  // directly - easy way to handle multi-number ID parts e.g. 3.1.15.32
  return replaceNode(visitor, node, id.split("."), 1);
};

const expandedNode = () => ({});
const unexpandedNode = () => undefined;

const toggleNodeById = (node, id) =>
  replaceNodeById(node, id, oldNode =>
    oldNode ? unexpandedNode() : expandedNode()
  );
const expandNodeById = (node, id) =>
  replaceNodeById(node, id, oldNode => expandedNode());

class Menu extends Component {
  constructor(props) {
    super(props);
    this.state = {
      menuItems: [],
      openMenus: {},
      checkedMenus: {}
    };
    this.handleMenuToggle = this.handleMenuToggle.bind(this);
    this.handleChecked = this.handleChecked.bind(this);
  }

  render() {
    const { menuItems, openMenus, checkedMenus } = this.state;

    return (
      <MenuItemContainer
        openMenus={openMenus}
        menuItems={menuItems}
        onMenuToggle={this.handleMenuToggle}
        checkedMenus={checkedMenus}
        onChecked={this.handleChecked}
      />
    );
  }

  componentDidMount() {
    const { initialOpenMenuId, initialCheckedMenuIds } = this.props;

    loadMenu().then(menuItems => {
      const initialMenuState = {};
      this.setState({
        menuItems,
        openMenus: expandNodeById(initialMenuState, initialOpenMenuId),
        checkedMenus: initialCheckedMenuIds.reduce(
          (acc, val) => ({ ...acc, [val]: true }),
          {}
        )
      });
    });
  }

  handleMenuToggle(toggledId) {
    this.setState(({ openMenus }) => ({
      openMenus: toggleNodeById(openMenus, toggledId)
    }));
  }

  handleChecked(toggledId) {
    this.setState(({ checkedMenus }) => ({
      checkedMenus: {
        ...checkedMenus,
        [toggledId]: checkedMenus[toggledId] ? unexpandedNode() : expandedNode()
      }
    }));
  }
}

function MenuItemContainer({
  openMenus,
  onMenuToggle,
  checkedMenus,
  onChecked,
  menuItems = []
}) {
  if (!menuItems.length) return null;

  const renderMenuItem = menuItem => (
    <li key={menuItem.id}>
      <MenuItem
        openMenus={openMenus}
        onMenuToggle={onMenuToggle}
        checkedMenus={checkedMenus}
        onChecked={onChecked}
        {...menuItem}
      />
    </li>
  );

  return <ul>{menuItems.map(renderMenuItem)}</ul>;
}

class MenuItem extends Component {
  constructor(props) {
    super(props);
    this.handleToggle = this.handleToggle.bind(this);
    this.handleChecked = this.handleChecked.bind(this);
  }

  render() {
    const {
      children,
      name,
      id,
      openMenus,
      onMenuToggle,
      checkedMenus,
      onChecked
    } = this.props;

    const isLastChild = !children;
    return (
      <Fragment>
        <Button onClick={isLastChild ? this.handleChecked : this.handleToggle}>
          {name}
        </Button>
        {isLastChild && (
          <Input
            addon
            type="checkbox"
            onChange={this.handleChecked}
            checked={!!checkedMenus[id]}
            value={id}
          />
        )}

        <Collapse isOpen={openMenus ? !!openMenus[id] : false}>
          <MenuItemContainer
            menuItems={children}
            // Pass down child menus' state
            openMenus={openMenus && openMenus[id]}
            onMenuToggle={onMenuToggle}
            checkedMenus={checkedMenus}
            onChecked={onChecked}
          />
        </Collapse>
      </Fragment>
    );
  }

  handleToggle() {
    this.props.onMenuToggle(this.props.id);
  }

  handleChecked() {
    this.props.onChecked(this.props.id);
  }
}

ReactDOM.render(
  <Menu initialOpenMenuId="3.1.1.1" initialCheckedMenuIds={["3.1.1.1.1"]} />,
  document.getElementById("root")
);
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reactstrap/8.4.1/reactstrap.min.js"></script>

<div id="root"></div>

Hướng dẫn

Trước khi bắt đầu, tôi phải nói rằng tôi đã tự do thay đổi một số mã để sử dụng các tính năng JavaScript hiện đại như phá hủy đối tượng , phá hủy mảng , phần còn lạicác giá trị mặc định .

Tạo trạng thái

Vì thế. Vì ID của các mục menu là các số được phân tách bằng dấu chấm, nên chúng ta có thể tận dụng lợi thế này khi xây dựng trạng thái. Trạng thái về cơ bản là một cấu trúc giống như cây, với mỗi menu phụ là con của cha mẹ và nút lá ("menu cuối cùng" hoặc "menu sâu nhất") có giá trị của {}nó nếu được mở rộng hoặc undefinednếu không. Đây là cách trạng thái ban đầu của menu được xây dựng:

<Menu initialOpenMenuId="3.1.1.1" initialCheckedMenuIds={["3.1.1.1.1"]} />

// ...

loadMenu().then(menuItems => {
  const initialMenuState = {};
  this.setState({
    menuItems,
    openMenus: expandNodeById(initialMenuState, initialOpenMenuId),
    checkedMenus: initialCheckedMenuIds.reduce(
      (acc, val) => ({ ...acc, [val]: true }),
      {}
    )
  });
});

// ...

const expandedNode = () => ({});
const unexpandedNode = () => undefined;

const toggleNodeById = (node, id) =>
  replaceNodeById(node, id, oldNode =>
    oldNode ? unexpandedNode() : expandedNode()
  );
const expandNodeById = (node, id) =>
  replaceNodeById(node, id, oldNode => expandedNode());

const replaceNodeById = (node, id, visitor) => {
  // Pass array of the id's parts instead of working on the string
  // directly - easy way to handle multi-number ID parts e.g. 3.1.15.32
  return replaceNode(visitor, node, id.split("."), 1);
};

const replaceNode = (replacer, node, idPath, i) => {
  if (i <= idPath.length && !node) {
    // Not at target node yet, create nodes in between
    node = {};
  }
  if (i > idPath.length) {
    // Reached target node
    return replacer(node);
  }

  // Construct ID that matches this depth - depth meaning
  // the amount of dots in between the ID
  const id = idPath.slice(0, i).join(".");
  return {
    ...node,
    // Recurse
    [id]: replaceNode(replacer, node[id], idPath, i + 1)
  };
};

Chúng ta hãy tách rời từng chút một.

const expandedNode = () => ({});
const unexpandedNode = () => undefined;

Đây chỉ là các hàm tiện lợi mà chúng tôi xác định để chúng tôi có thể dễ dàng thay đổi giá trị chúng tôi sử dụng để thể hiện một nút mở rộng và chưa được mở rộng. Nó cũng làm cho mã dễ đọc hơn một chút so với việc chỉ sử dụng bằng chữ {}hoặc undefinedtrong mã. Các giá trị mở rộng và chưa được mở rộng cũng có thể là như vậy , truefalseđiều quan trọng là nút mở rộng là trung thực và nút chưa được mở rộng là sai lệch . Thêm về điều đó sau.

const toggleNodeById = (node, id) =>
  replaceNodeById(node, id, oldNode =>
    oldNode ? unexpandedNode() : expandedNode()
  );
const expandNodeById = (node, id) =>
  replaceNodeById(node, id, oldNode => expandedNode());

Các chức năng này cho phép chúng tôi chuyển đổi hoặc mở rộng một menu cụ thể trong trạng thái menu. Tham số đầu tiên là chính trạng thái menu, thứ hai là ID chuỗi của menu (ví dụ "3.1.1.1.1") và thứ ba là chức năng thay thế. Hãy nghĩ về điều này giống như chức năng bạn chuyển đến .map(). Chức năng thay thế được tách ra khỏi lần lặp cây đệ quy thực tế để bạn có thể dễ dàng thực hiện nhiều chức năng hơn sau này - ví dụ: nếu bạn muốn một số menu cụ thể được mở rộng, bạn có thể chuyển vào một hàm trả về unexpandedNode().

const replaceNodeById = (node, id, visitor) => {
  // Pass array of the id's parts instead of working on the string
  // directly - easy way to handle multi-number ID parts e.g. 3.1.15.32
  return replaceNode(visitor, node, id.split("."), 1);
};

Chức năng này được sử dụng bởi hai cái trước để cung cấp giao diện sạch hơn. ID được phân chia ở đây bởi các dấu chấm ( .) cung cấp cho chúng ta một mảng các phần ID. Hàm tiếp theo hoạt động trên mảng này thay vì chuỗi ID trực tiếp, bởi vì theo cách đó chúng ta không cần phải thực hiện .indexOf('.')shenanigans.

const replaceNode = (replacer, node, idPath, i) => {
  if (i <= idPath.length && !node) {
    // Not at target node yet, create nodes in between
    node = {};
  }
  if (i > idPath.length) {
    // Reached target node
    return replacer(node);
  }

  // Construct ID that matches this depth - depth meaning
  // the amount of dots in between the ID
  const id = idPath.slice(0, i).join(".");
  return {
    ...node,
    // Recurse
    [id]: replaceNode(replacer, node[id], idPath, i + 1)
  };
};

Các replaceNodechức năng là thịt của vấn đề. Đây là một hàm đệ quy tạo ra một cây mới từ cây menu cũ, thay thế nút đích cũ bằng hàm thay thế được cung cấp. Nếu cây bị thiếu các phần ở giữa, ví dụ như khi cây {}nhưng chúng ta muốn thay thế nút 3.1.1.1, nó sẽ tạo các nút cha ở giữa. Kiểu như mkdir -pnếu bạn quen thuộc với lệnh.

Vì vậy, đó là trạng thái menu. Trạng thái hộp kiểm ( checkedMenus) về cơ bản chỉ là một chỉ mục, với khóa là ID và giá trị truenếu một mục được chọn. Trạng thái này không được đệ quy, vì chúng không cần phải được kiểm tra hoặc kiểm tra đệ quy. Nếu bạn quyết định rằng bạn muốn hiển thị một chỉ báo rằng một cái gì đó trong mục menu này được chọn, một giải pháp dễ dàng là thay đổi trạng thái hộp kiểm để được đệ quy như trạng thái menu.

Kết xuất cây

Thành <Menu>phần này truyền xuống các trạng thái <MenuItemContainer>, làm cho <MenuItem>s.

function MenuItemContainer({
  openMenus,
  onMenuToggle,
  checkedMenus,
  onChecked,
  menuItems = []
}) {
  if (!menuItems.length) return null;

  const renderMenuItem = menuItem => (
    <li key={menuItem.id}>
      <MenuItem
        openMenus={openMenus}
        onMenuToggle={onMenuToggle}
        checkedMenus={checkedMenus}
        onChecked={onChecked}
        {...menuItem}
      />
    </li>
  );

  return <ul>{menuItems.map(renderMenuItem)}</ul>;
}

Thành <MenuItemContainer>phần này không khác lắm so với thành phần ban đầu. Các <MenuItem>thành phần trông hơi khác nhau, mặc dù.

class MenuItem extends Component {
  constructor(props) {
    super(props);
    this.handleToggle = this.handleToggle.bind(this);
    this.handleChecked = this.handleChecked.bind(this);
  }

  render() {
    const {
      children,
      name,
      id,
      openMenus,
      onMenuToggle,
      checkedMenus,
      onChecked
    } = this.props;

    const isLastChild = !children;
    return (
      <Fragment>
        <Button onClick={isLastChild ? this.handleChecked : this.handleToggle}>
          {name}
        </Button>
        {isLastChild && (
          <Input
            addon
            type="checkbox"
            onChange={this.handleChecked}
            checked={!!checkedMenus[id]}
            value={id}
          />
        )}

        <Collapse isOpen={openMenus ? !!openMenus[id] : false}>
          <MenuItemContainer
            menuItems={children}
            // Pass down child menus' state
            openMenus={openMenus && openMenus[id]}
            onMenuToggle={onMenuToggle}
            checkedMenus={checkedMenus}
            onChecked={onChecked}
          />
        </Collapse>
      </Fragment>
    );
  }

  handleToggle() {
    this.props.onMenuToggle(this.props.id);
  }

  handleChecked() {
    this.props.onChecked(this.props.id);
  }
}

Ở đây phần quan trọng là đây : openMenus={openMenus && openMenus[id]}. Thay vì chuyển xuống toàn bộ trạng thái menu, chúng tôi chỉ chuyển xuống cây trạng thái chứa con của mục hiện tại. Điều này cho phép thành phần rất dễ dàng kiểm tra xem nó nên được mở hay thu gọn - chỉ cần kiểm tra xem ID của chính nó có được tìm thấy từ đối tượng không ( openMenus ? !!openMenus[id] : false)!

Tôi cũng đã thay đổi nút chuyển đổi để chuyển đổi hộp kiểm thay vì trạng thái menu nếu đó là mục sâu nhất trong menu - nếu đây không phải là thứ bạn đang tìm kiếm, thì khá nhanh để thay đổi lại.

Tôi cũng sử dụng !!ở đây để ép buộc {}undefinedtừ trạng thái menu vào truehoặc false. Đây là lý do tại sao tôi nói nó chỉ quan trọng cho dù chúng là sự thật hay giả. Các reactstrapthành phần dường như muốn rõ ràng truehoặc falsethay vì trung thực / giả dối, vì vậy đó là lý do tại sao nó ở đó.

Và cuối cùng:

ReactDOM.render(
  <Menu initialOpenMenuId="3.1.1.1" initialCheckedMenuIds={["3.1.1.1.1"]} />,
  document.getElementById("root")
);

Ở đây chúng tôi vượt qua trạng thái ban đầu để <Menu>. Đây initialOpenMenuIdcũng có thể là một mảng (hoặc initialCheckedMenuIdscó thể là một chuỗi đơn), nhưng điều này phù hợp với thông số kỹ thuật của câu hỏi.

Phòng cải tiến

Giải pháp ngay bây giờ chuyển xuống rất nhiều trạng thái, chẳng hạn như onMenuToggleonCheckedgọi lại, và checkedMenustrạng thái không đệ quy. Chúng có thể sử dụng Bối cảnh của React .


1
Cảm ơn bạn đã dành nhiều thời gian và đưa ra giải pháp rất chi tiết ..

0

const loadMenu = () => Promise.resolve([{id:"1",name:"One",children:[{id:"1.1",name:"One - one",children:[{id:"1.1.1",name:"One - one - one"},{id:"1.1.2",name:"One - one - two"},{id:"1.1.3",name:"One - one - three"}]}]},{id:"2",name:"Two",children:[{id:"2.1",name:"Two - one"}]},{id:"3",name:"Three",children:[{id:"3.1",name:"Three - one",children:[{id:"3.1.1",name:"Three - one - one",children:[{id:"3.1.1.1",name:"Three - one - one - one",children:[{id:"3.1.1.1.1",name:"Three - one - one - one - one"}]}]}]}]},{id:"4",name:"Four"},{id:"5",name:"Five",children:[{id:"5.1",name:"Five - one"},{id:"5.2",name:"Five - two"},{id:"5.3",name:"Five - three"},{id:"5.4",name:"Five - four"}]},{id:"6",name:"Six"}]);

const openMenuId = "3.1.1.1.1";

const {Component, Fragment} = React;
const {Button, Collapse, Input} = Reactstrap;

class Menu extends Component {
  constructor(props) {
    super(props);
    this.state = {menuItems: []};
  }

  render() {
    return <MenuItemContainer menuItems={this.state.menuItems} />;
  }

  componentDidMount() {
    loadMenu().then(menuItems => this.setState({menuItems}));
  }
}

function MenuItemContainer(props) {
  if (!props.menuItems.length) return null;
  
  const renderMenuItem = menuItem =>
    <li key={menuItem.id}><MenuItem {...menuItem} /></li>;
    
  return <ul>{props.menuItems.map(renderMenuItem)}</ul>;
}
MenuItemContainer.defaultProps = {menuItems: []};

class MenuItem extends Component {
  constructor(props) {
    super(props);
    this.state = {isOpen: false};
    this.toggle = this.toggle.bind(this);
  }

  render() {
    let isLastChild = this.props.children ? false : true;
    let {isOpen} = this.state;
    if(openMenuId.startsWith(this.props.id)){isOpen = true;}
    return (
      <Fragment>
        <Button onClick={this.toggle}>{this.props.name}</Button>
        <Fragment>
          {isLastChild ? <Input type="checkbox" checked={openMenuId === this.props.id} value={this.props.id} /> : ''}
        </Fragment>
        <Collapse isOpen={isOpen}>
          <MenuItemContainer menuItems={this.props.children} />
        </Collapse>
      </Fragment>
    );
  }

  toggle() {
    this.setState(({isOpen}) => ({isOpen: !isOpen}));
  }
}

ReactDOM.render(<Menu />, document.getElementById("root"));
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reactstrap/8.4.1/reactstrap.min.js"></script>

<div id="root"></div>


Mã bãi không phải là câu trả lời hữu ích . Nói những gì bạn đã làm, và làm thế nào nó hoạt động.
TJ Crowder

@TJCrowder, tôi cũng không thể đóng các mục menu đã mở này .. Và cũng như bạn đã nói câu trả lời này không có nhiều thông tin .. Như đã yêu cầu trước đó, bạn có thể vui lòng giúp tôi đạt được kết quả không ??

Giải pháp này không giải quyết được hoàn toàn các vấn đề của tôi, vì nó mở cấp độ cuối cùng trong menu ba nhưng tôi không thể làm cho nó sụp đổ trở lại và cũng không thể thực hiện bất kỳ hộp kiểm nào khác để kiểm tra nó .. Nhưng đánh giá cao sự giúp đỡ của bạn nhưng không may 't công việc đầy đủ như mong đợi ..

0

Giả sử bạn chỉ cần mở một số menu nhất định khi bắt đầu, bạn có thể đặt MenuItemthành phần để mong đợi một thuộc tính boolean defaultOpenvà sử dụng nó để đặt ban đầu isOpen.

Sau đó, tất cả những gì chúng ta cần làm là đặt thuộc tính này menuItemsvào tải.

import React from 'react'
import { Button, Collapse, Input } from 'reactstrap';
import 'bootstrap/dist/css/bootstrap.min.css';

const loadMenu = () => Promise.resolve([{id:"1",name:"One",children:[{id:"1.1",name:"One - one",children:[{id:"1.1.1",name:"One - one - one"},{id:"1.1.2",name:"One - one - two"},{id:"1.1.3",name:"One - one - three"}]}]},{id:"2",name:"Two",children:[{id:"2.1",name:"Two - one"}]},{id:"3",name:"Three",children:[{id:"3.1",name:"Three - one",children:[{id:"3.1.1",name:"Three - one - one",children:[{id:"3.1.1.1",name:"Three - one - one - one",children:[{id:"3.1.1.1.1",name:"Three - one - one - one - one"}]}]}]}]},{id:"4",name:"Four"},{id:"5",name:"Five",children:[{id:"5.1",name:"Five - one"},{id:"5.2",name:"Five - two"},{id:"5.3",name:"Five - three"},{id:"5.4",name:"Five - four"}]},{id:"6",name:"Six"}]);

const openMenuId = "3.1.1.1";

const {Component, Fragment} = React;

function setDefaultOpen(menuItems, openMenuId) {
  if(!menuItems) return
  const openMenuItem = menuItems.find(item => openMenuId.startsWith(item.id))
  if(!openMenuItem) return
  openMenuItem.defaultOpen = true
  setDefaultOpen(openMenuItem.children, openMenuId)
}

export default class Menu extends Component {
  constructor(props) {
    super(props);
    this.state = {menuItems: []};
  }

  render() {
    return <MenuItemContainer menuItems={this.state.menuItems} />;
  }

  componentDidMount() {
    loadMenu().then(menuItems => {
      setDefaultOpen(menuItems, openMenuId)
      this.setState({menuItems})
    });
  }
}

function MenuItemContainer(props) {
  if (!props.menuItems.length) return null;

  const renderMenuItem = menuItem =>
    <li key={menuItem.id}><MenuItem {...menuItem} /></li>;

  return <ul>{props.menuItems.map(renderMenuItem)}</ul>;
}
MenuItemContainer.defaultProps = {menuItems: []};

class MenuItem extends Component {
  constructor(props) {
    super(props);
    this.state = {isOpen: props.defaultOpen};
    this.toggle = this.toggle.bind(this);
  }

  render() {
    let isLastChild = this.props.children ? false : true;
    return (
      <Fragment>
        <Button onClick={this.toggle}>{this.props.name}</Button>
        <Fragment>
          {isLastChild ? <Input type="checkbox" value={this.props.id} /> : ''}
        </Fragment>
        <Collapse isOpen={this.state.isOpen}>
          <MenuItemContainer menuItems={this.props.children} />
        </Collapse>
      </Fragment>
    );
  }

  toggle() {
    this.setState(({isOpen}) => ({isOpen: !isOpen}));
  }
}

Nếu bạn cần mở khả năng mở một mục menu sau khi kết xuất ban đầu, thì bạn cần tạo MenuItemmột thành phần được kiểm soát.

tức là kéo isOpentrạng thái lên cha mẹ và chuyển nó xuống MenuItemthành phần dưới dạng prop cùng với chức năng gọi lại mà nó sẽ gọi khi nhấp vào chuyển id của nó làm đối số. Hàm gọi lại trong cha mẹ sẽ chuyển đổi thuộc isOpentính của mục với id đã cho ở trạng thái.


-1

chỉ cần thêm một lớp .actvehoặc bất kỳ khác mà bạn muốn và tạo kiểu theo requirment của bạn và sau đó thêm scriptnếu bạn đang sử dụng bình thường jssau đó document.querySelector("youElementClassOrId").classList.toggle("idOrClassYouWantToToggle"). Tôi hy vọng điều này sẽ làm việc


Không, đây không phải là thứ tôi cần .. Ngoài ra, việc chuyển đổi và hoạt động tốt .. Tôi cần so sánh với các giá trị và cần làm cho hộp kiểm tra được mở và mở các menu lên đến cấp độ cha mẹ từ một người được chọn ..
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.