仅切换在 Reactjs 中单击的菜单
Toggle only the menu clicked in Reactjs
我正在使用 recursion function
制作菜单和子菜单,我需要帮助才能打开相应的菜单和子菜单..
用于按钮和折叠 Reactstrap 已被使用..
做菜单填充的递归函数:
{this.state.menuItems &&
this.state.menuItems.map((item, index) => {
return (
<div key={item.id}>
<Button onClick={this.toggle.bind(this)}> {item.name} </Button>
<Collapse isOpen={this.state.isToggleOpen}>
{this.buildMenu(item.children)}
</Collapse>
</div>
);
})}
而buildMenu
函数如下,
buildMenu(items) {
return (
<ul>
{items &&
items.map(item => (
<li key={item.id}>
<div>
{this.state.isToggleOpen}
<Button onClick={this.toggle.bind(this)}> {item.name} </Button>
<Collapse isOpen={this.state.isToggleOpen}>
{item.children && item.children.length > 0
? this.buildMenu(item.children)
: null}
</Collapse>
</div>
</li>
))}
</ul>
);
}
目前代码没有问题,但我需要帮助 menu -> submenu -> submenu
逐步打开和关闭相应级别。
工作示例:https://codesandbox.io/s/reactstrap-accordion-9epsp
你可以看看这个例子,当你点击任何菜单时,整个级别的菜单都会打开,而不是点击一个。
要求
如果用户单击菜单 One
,则子菜单(子)
-> One-One
需要打开。
然后如果用户点击 One-One
,
-> One-One-One
-> One - one - two
-> One - one - three
需要打开。
同样,它是嵌套的,因此在单击任何菜单/子项后,需要打开它们各自的下一级。
我是 React 和 Reactstrap 设计方式的新手,所以来自专业知识的任何帮助对我来说都是有用的,可以帮助我继续并了解实际需要如何完成。
您需要创建一个内部组件来管理每个级别的状态。
例如,考虑以下功能组件(我将把它留给您转换为 class 组件):
const MenuButton = ({ name, children }) => {
const [open, setOpen] = useState(false);
const toggle = useCallback(() => setOpen(o => !o), [setOpen]);
return (
<>
<Button onClick={toggle}>{name}</Button>
<Collapse open={open}>{children}</Collapse>
</>
);
};
该组件将管理是否显示其子组件。用它代替所有 <div><Button/><Collapse/></div>
部分,它将管理每个级别的打开状态。
将共享状态保持在顶部,但如果您不需要知道某些内容是否针对其他逻辑进行了扩展,请将其保持在本地。
此外,如果您确实需要父组件中的信息,请使用您已有的预定义对象并向其添加默认为 false 的 'open' 字段。单击后,对该对象设置状态以正确标记适当的对象以在打开时具有 true 参数。
虽然本地化状态更清晰。
扩展示例
import React, { Component, useState, useCallback, Fragment } from "react";
import { Collapse, Button } from "reactstrap";
import { loadMenu } from "./service";
const MenuButton = ({ name, children }) => {
const [open, setOpen] = React.useState(false);
const toggle = useCallback(() => setOpen(o => !o), [setOpen]);
return (
<Fragment>
<Button onClick={toggle}>{name}</Button>
<Collapse open={open}>{children}</Collapse>
</Fragment>
);
};
class Hello extends Component {
constructor(props) {
super(props);
this.state = {
currentSelection: "",
menuItems: [],
};
}
componentDidMount() {
loadMenu().then(items => this.setState({ menuItems: items }));
}
buildMenu(items) {
return (
<ul>
{items &&
items.map(item => (
<li key={item.id}>
<MenuButton name={item.name}>
{item.children && item.children.length > 0
? this.buildMenu(item.children)
: null}
</MenuButton>
</li>
))}
</ul>
);
}
render() {
return (
<div>
<h2>Click any of the below option</h2>
{this.state.menuItems &&
this.state.menuItems.map((item, index) => {
return (
<MenuButton name={item.name}>
{this.buildMenu(item.children)}
</MenuButton>
);
})}
</div>
);
}
}
export default Hello;
与其使用一个大组件,不如考虑将您的组件拆分成更小的组件。这样您就可以为每个菜单项添加状态以切换底层菜单项。
如果您想将所有底层菜单项重置为其默认关闭位置,您应该在每次打开底层按钮时创建一个新的组件实例。通过让 <MenuItemContainer key={timesOpened}
,MenuItemContainer
将在您 "open" MenuItem
时分配一个新密钥。分配一个新键将创建一个新的组件实例,而不是更新现有的实例。
有关详细说明,我建议阅读 You Probably Don't Need Derived State - Recommendation: Fully uncontrolled component with a key
。
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} = Reactstrap;
class Menu extends Component {
constructor(props) {
super(props);
this.state = {menuItems: []};
}
render() {
const {menuItems} = this.state;
return <MenuItemContainer menuItems={menuItems} />;
}
componentDidMount() {
loadMenu().then(menuItems => this.setState({menuItems}));
}
}
class MenuItemContainer extends Component {
render() {
const {menuItems} = this.props;
if (!menuItems.length) return null;
return <ul>{menuItems.map(this.renderMenuItem)}</ul>;
}
renderMenuItem(menuItem) {
const {id} = menuItem;
return <li key={id}><MenuItem {...menuItem} /></li>;
}
}
MenuItemContainer.defaultProps = {menuItems: []};
class MenuItem extends Component {
constructor(props) {
super(props);
this.state = {isOpen: false, timesOpened: 0};
this.open = this.open.bind(this);
this.close = this.close.bind(this);
}
render() {
const {name, children} = this.props;
const {isOpen, timesOpened} = this.state;
return (
<Fragment>
<Button onClick={isOpen ? this.close : this.open}>{name}</Button>
<Collapse isOpen={isOpen}>
<MenuItemContainer key={timesOpened} menuItems={children} />
</Collapse>
</Fragment>
);
}
open() {
this.setState(({timesOpened}) => ({
isOpen: true,
timesOpened: timesOpened + 1,
}));
}
close() {
this.setState({isOpen: false});
}
}
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>
我正在使用 recursion function
制作菜单和子菜单,我需要帮助才能打开相应的菜单和子菜单..
用于按钮和折叠 Reactstrap 已被使用..
做菜单填充的递归函数:
{this.state.menuItems &&
this.state.menuItems.map((item, index) => {
return (
<div key={item.id}>
<Button onClick={this.toggle.bind(this)}> {item.name} </Button>
<Collapse isOpen={this.state.isToggleOpen}>
{this.buildMenu(item.children)}
</Collapse>
</div>
);
})}
而buildMenu
函数如下,
buildMenu(items) {
return (
<ul>
{items &&
items.map(item => (
<li key={item.id}>
<div>
{this.state.isToggleOpen}
<Button onClick={this.toggle.bind(this)}> {item.name} </Button>
<Collapse isOpen={this.state.isToggleOpen}>
{item.children && item.children.length > 0
? this.buildMenu(item.children)
: null}
</Collapse>
</div>
</li>
))}
</ul>
);
}
目前代码没有问题,但我需要帮助 menu -> submenu -> submenu
逐步打开和关闭相应级别。
工作示例:https://codesandbox.io/s/reactstrap-accordion-9epsp
你可以看看这个例子,当你点击任何菜单时,整个级别的菜单都会打开,而不是点击一个。
要求
如果用户单击菜单 One
,则子菜单(子)
-> One-One
需要打开。
然后如果用户点击 One-One
,
-> One-One-One
-> One - one - two
-> One - one - three
需要打开。
同样,它是嵌套的,因此在单击任何菜单/子项后,需要打开它们各自的下一级。
我是 React 和 Reactstrap 设计方式的新手,所以来自专业知识的任何帮助对我来说都是有用的,可以帮助我继续并了解实际需要如何完成。
您需要创建一个内部组件来管理每个级别的状态。
例如,考虑以下功能组件(我将把它留给您转换为 class 组件):
const MenuButton = ({ name, children }) => {
const [open, setOpen] = useState(false);
const toggle = useCallback(() => setOpen(o => !o), [setOpen]);
return (
<>
<Button onClick={toggle}>{name}</Button>
<Collapse open={open}>{children}</Collapse>
</>
);
};
该组件将管理是否显示其子组件。用它代替所有 <div><Button/><Collapse/></div>
部分,它将管理每个级别的打开状态。
将共享状态保持在顶部,但如果您不需要知道某些内容是否针对其他逻辑进行了扩展,请将其保持在本地。
此外,如果您确实需要父组件中的信息,请使用您已有的预定义对象并向其添加默认为 false 的 'open' 字段。单击后,对该对象设置状态以正确标记适当的对象以在打开时具有 true 参数。
虽然本地化状态更清晰。
扩展示例
import React, { Component, useState, useCallback, Fragment } from "react";
import { Collapse, Button } from "reactstrap";
import { loadMenu } from "./service";
const MenuButton = ({ name, children }) => {
const [open, setOpen] = React.useState(false);
const toggle = useCallback(() => setOpen(o => !o), [setOpen]);
return (
<Fragment>
<Button onClick={toggle}>{name}</Button>
<Collapse open={open}>{children}</Collapse>
</Fragment>
);
};
class Hello extends Component {
constructor(props) {
super(props);
this.state = {
currentSelection: "",
menuItems: [],
};
}
componentDidMount() {
loadMenu().then(items => this.setState({ menuItems: items }));
}
buildMenu(items) {
return (
<ul>
{items &&
items.map(item => (
<li key={item.id}>
<MenuButton name={item.name}>
{item.children && item.children.length > 0
? this.buildMenu(item.children)
: null}
</MenuButton>
</li>
))}
</ul>
);
}
render() {
return (
<div>
<h2>Click any of the below option</h2>
{this.state.menuItems &&
this.state.menuItems.map((item, index) => {
return (
<MenuButton name={item.name}>
{this.buildMenu(item.children)}
</MenuButton>
);
})}
</div>
);
}
}
export default Hello;
与其使用一个大组件,不如考虑将您的组件拆分成更小的组件。这样您就可以为每个菜单项添加状态以切换底层菜单项。
如果您想将所有底层菜单项重置为其默认关闭位置,您应该在每次打开底层按钮时创建一个新的组件实例。通过让 <MenuItemContainer key={timesOpened}
,MenuItemContainer
将在您 "open" MenuItem
时分配一个新密钥。分配一个新键将创建一个新的组件实例,而不是更新现有的实例。
有关详细说明,我建议阅读 You Probably Don't Need Derived State - Recommendation: Fully uncontrolled component with a key
。
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} = Reactstrap;
class Menu extends Component {
constructor(props) {
super(props);
this.state = {menuItems: []};
}
render() {
const {menuItems} = this.state;
return <MenuItemContainer menuItems={menuItems} />;
}
componentDidMount() {
loadMenu().then(menuItems => this.setState({menuItems}));
}
}
class MenuItemContainer extends Component {
render() {
const {menuItems} = this.props;
if (!menuItems.length) return null;
return <ul>{menuItems.map(this.renderMenuItem)}</ul>;
}
renderMenuItem(menuItem) {
const {id} = menuItem;
return <li key={id}><MenuItem {...menuItem} /></li>;
}
}
MenuItemContainer.defaultProps = {menuItems: []};
class MenuItem extends Component {
constructor(props) {
super(props);
this.state = {isOpen: false, timesOpened: 0};
this.open = this.open.bind(this);
this.close = this.close.bind(this);
}
render() {
const {name, children} = this.props;
const {isOpen, timesOpened} = this.state;
return (
<Fragment>
<Button onClick={isOpen ? this.close : this.open}>{name}</Button>
<Collapse isOpen={isOpen}>
<MenuItemContainer key={timesOpened} menuItems={children} />
</Collapse>
</Fragment>
);
}
open() {
this.setState(({timesOpened}) => ({
isOpen: true,
timesOpened: timesOpened + 1,
}));
}
close() {
this.setState({isOpen: false});
}
}
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>