Я пытаюсь реализовать подменю (вложенное меню).
Стоит упомянуть, что я использую компонент 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 (бонус). Если это не проблема, я был бы признателен, если бы кто-нибудь мог направить меня в правильном направлении, с чего начать изучение в первую очередь, чтобы я мог менее болезненно решать проблемы, связанные с этим удивительным фреймворком :)





Я искал тот же вопрос. Но не удалось найти реализацию вложенного меню. Так что я написал свой. Проверьте код ниже;
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);