Вложенное меню (подменю)

Я пытаюсь реализовать подменю (вложенное меню).

Стоит упомянуть, что я использую компонент Hydra и не имею предыдущего опыта работы с redux (начал изучать его несколько дней назад из-за этой конкретной проблемы).

Я следовал примеру, приведенному в material-ui для вложенного списка https://material-ui.com/demos/lists/#nested-list. И учебник от https://marmelab.com/react-admin/Theming.html#using-a-custom-menu для реализации настраиваемого меню.

Итак, у меня есть несколько вопросов.

1) Могу ли я иметь компонент с отслеживанием состояния (MyMenu) только для управления переключением пунктов меню?

Пример не имеет отношения к response-admin, но это просто пример того, что я имею в виду.

import React, { Component } from "react";
import { connect } from "react-redux";
import { addArticle } from "../actions/index";
const mapDispatchToProps = dispatch => {
  return {
    addArticle: article => dispatch(addArticle(article))
  };
};
class ConnectedForm extends Component {
  constructor() {
    super();
    this.state = {
      title: ""
    };
    this.handleChangeEvent = this.handleChangeEvent.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
  handleChangeEvent(event) {
    this.setState({ [event.target.id]: event.target.value });
  }
  handleSubmit(event) {
    event.preventDefault();
    const { title } = this.state;
    const id = uuidv1();
    this.props.addArticle({ title, id });
    this.setState({ title: "" });
  }
  render() {
    const { title } = this.state;
    return (
      <form onSubmit = {this.handleSubmit}>
        <div className = "form-group">
          <label htmlFor = "title">Title</label>
          <input
            type = "text"
            className = "form-control"
            id = "title"
            value = {title}
            onChange = {this.handleChangeEvent}
          />
        </div>
        <button type = "submit" className = "btn btn-success btn-lg">
          SAVE
        </button>
      </form>
    );
  }
}
const Form = connect(null, mapDispatchToProps)(ConnectedForm);
export default Form;

2) Если нет, могу ли я добиться этого, объявив новое состояние в магазине, например, open: false, а затем используя настраиваемый редуктор для его обработки.

3 (бонус). Если это не проблема, я был бы признателен, если бы кто-нибудь мог направить меня в правильном направлении, с чего начать изучение в первую очередь, чтобы я мог менее болезненно решать проблемы, связанные с этим удивительным фреймворком :)

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
7
0
10 893
2

Ответы 2

Я искал тот же вопрос. Но не удалось найти реализацию вложенного меню. Так что я написал свой. Проверьте код ниже;

import React, { Component, createElement } from "react";
import {
  Admin,
  Resource,
  Layout,
  MenuItemLink,
  getResources
} from "react-admin";
import jsonServerProvider from "ra-data-json-server";
import { withRouter } from "react-router-dom";
import { connect } from "react-redux";
import {
  List,
  ListItem,
  Collapse,
  ListItemText,
  ListItemIcon
} from "@material-ui/core";
import { ExpandLess, ExpandMore, StarBorder, LabelIcon } from "@material-ui/icons";
import { withStyles } from "@material-ui/core/styles";

const menuStyles = theme => ({
  nested: {
    paddingLeft: theme.spacing.unit * 4
  }
});

class Menu extends Component {
  menuList = [
    { name: "A", label: "Top menu 1", icon: <LabelIcon /> },
    { name: "B", label: "Top menu 2", icon: <LabelIcon /> },
    { name: "c", label: "Top menu 3", icon: <LabelIcon /> }
  ];
  constructor(props) {
    super(props);
    this.state = { open: "A" };
  }
  render() {
    const { resources, onMenuClick, logout } = this.props;
    return (
      <div>
        <List component = "nav">
          {this.menuList.map(menu => {
            return (
              <div key = {menu.name}>
                <ListItem
                  button
                  onClick = {() => this.setState(state => ({ open: menu.name }))}
                >
                  <ListItemIcon>{menu.icon}</ListItemIcon>
                  <ListItemText inset primary = {menu.label} />
                  {this.state.open == menu.name ? (
                    <ExpandLess />
                  ) : (
                    <ExpandMore />
                  )}
                </ListItem>
                <Collapse
                  in = {this.state.open == menu.name}
                  timeout = "auto"
                  unmountOnExit
                >
                  <List component = "div" disablePadding>
                    {resources
                      .filter(x => x.options.menu == menu.name)
                      .map((resource, i) => (
                        <MenuItemLink
                          key = {"m" + i}
                          to = {`/${resource.name}`}
                          primaryText = {resource.options.label || resource.name}
                          leftIcon = {
                            resource.icon
                              ? createElement(resource.icon)
                              : undefined
                          }
                          onClick = {onMenuClick}
                          className = {this.props.classes.nested}
                        />
                      ))}
                  </List>
                </Collapse>
              </div>
            );
          })}
        </List>
      </div>
    );
  }
}

var MenuWithStyles = withStyles(menuStyles)(Menu);

const MyMenu = withRouter(
  connect(state => ({
    resources: getResources(state)
  }))(MenuWithStyles)
);

const MyLayout = props => <Layout {...props} menu = {MyMenu} />;

const App = () => (
    <Admin      
      ...
      appLayout = {MyLayout}
    >
      <Resource 
        ...
        options = {{ label: 'Page 1' menu: "A" }}
      />
      <Resource 
        ...
        options = {{ label: 'Page 2' menu: "A" }}
      />
      <Resource 
        ...
        options = {{ label: 'Page 3' menu: "B" }}
      />
    </Admin>
  );

Демонстрация response-admin теперь показывает способ сделать это в примеры / демо / SRC / макет / Menu.js:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import compose from 'recompose/compose';
import SettingsIcon from '@material-ui/icons/Settings';
import LabelIcon from '@material-ui/icons/Label';
import { withRouter } from 'react-router-dom';
import {
    translate,
    DashboardMenuItem,
    MenuItemLink,
    Responsive,
} from 'react-admin';

import visitors from '../visitors';
import orders from '../orders';
import invoices from '../invoices';
import products from '../products';
import categories from '../categories';
import reviews from '../reviews';
import SubMenu from './SubMenu';

class Menu extends Component {
    state = {
        menuCatalog: false,
        menuSales: false,
        menuCustomers: false,
    };

    static propTypes = {
        onMenuClick: PropTypes.func,
        logout: PropTypes.object,
    };

    handleToggle = menu => {
        this.setState(state => ({ [menu]: !state[menu] }));
    };

    render() {
        const { onMenuClick, open, logout, translate } = this.props;
        return (
            <div>
                {' '}
                <DashboardMenuItem onClick = {onMenuClick} />
                <SubMenu
                    handleToggle = {() => this.handleToggle('menuSales')}
                    isOpen = {this.state.menuSales}
                    sidebarIsOpen = {open}
                    name = "pos.menu.sales"
                    icon = {<orders.icon />}
                >
                    <MenuItemLink
                        to = {`/commands`}
                        primaryText = {translate(`resources.commands.name`, {
                            smart_count: 2,
                        })}
                        leftIcon = {<orders.icon />}
                        onClick = {onMenuClick}
                    />
                    <MenuItemLink
                        to = {`/invoices`}
                        primaryText = {translate(`resources.invoices.name`, {
                            smart_count: 2,
                        })}
                        leftIcon = {<invoices.icon />}
                        onClick = {onMenuClick}
                    />
                </SubMenu>
                <SubMenu
                    handleToggle = {() => this.handleToggle('menuCatalog')}
                    isOpen = {this.state.menuCatalog}
                    sidebarIsOpen = {open}
                    name = "pos.menu.catalog"
                    icon = {<products.icon />}
                >
                    <MenuItemLink
                        to = {`/products`}
                        primaryText = {translate(`resources.products.name`, {
                            smart_count: 2,
                        })}
                        leftIcon = {<products.icon />}
                        onClick = {onMenuClick}
                    />
                    <MenuItemLink
                        to = {`/categories`}
                        primaryText = {translate(`resources.categories.name`, {
                            smart_count: 2,
                        })}
                        leftIcon = {<categories.icon />}
                        onClick = {onMenuClick}
                    />
                </SubMenu>
                <SubMenu
                    handleToggle = {() => this.handleToggle('menuCustomer')}
                    isOpen = {this.state.menuCustomer}
                    sidebarIsOpen = {open}
                    name = "pos.menu.customers"
                    icon = {<visitors.icon />}
                >
                    <MenuItemLink
                        to = {`/customers`}
                        primaryText = {translate(`resources.customers.name`, {
                            smart_count: 2,
                        })}
                        leftIcon = {<visitors.icon />}
                        onClick = {onMenuClick}
                    />
                    <MenuItemLink
                        to = {`/segments`}
                        primaryText = {translate(`resources.segments.name`, {
                            smart_count: 2,
                        })}
                        leftIcon = {<LabelIcon />}
                        onClick = {onMenuClick}
                    />
                </SubMenu>
                <MenuItemLink
                    to = {`/reviews`}
                    primaryText = {translate(`resources.reviews.name`, {
                        smart_count: 2,
                    })}
                    leftIcon = {<reviews.icon />}
                    onClick = {onMenuClick}
                />
                <Responsive
                    xsmall = {
                        <MenuItemLink
                            to = "/configuration"
                            primaryText = {translate('pos.configuration')}
                            leftIcon = {<SettingsIcon />}
                            onClick = {onMenuClick}
                        />
                    }
                    medium = {null}
                />
                <Responsive
                    small = {logout}
                    medium = {null} // Pass null to render nothing on larger devices
                />
            </div>
        );
    }
}

const mapStateToProps = state => ({
    open: state.admin.ui.sidebarOpen,
    theme: state.theme,
    locale: state.i18n.locale,
});

const enhance = compose(
    withRouter,
    connect(
        mapStateToProps,
        {}
    ),
    translate
);

export default enhance(Menu);

Другие вопросы по теме