Ant-design-pro: 怎么实现异步获取菜单数据

Created on 18 Jan 2018  ·  30Comments  ·  Source: ant-design/ant-design-pro

想异步加载左侧菜单的数据,怎么实现呢?

Most helpful comment

1.先创建高阶组件App

import React from 'react';
import {connect} from "dva";
import {Spin} from "antd";

const App = (WrappedComponent) => {
  @connect(state => ({
    global: state.global,
  }))
  class App extends React.Component {
    componentDidMount() {
      this.props.dispatch({
        type: 'global/fetchMenus'
      })
    }
    render() {
      const menus = this.props.global.menus;

      return (
        menus.length === 0 ? <Spin spinning={true}/>:
          <WrappedComponent {...this.props} menus={menus}/>
      )
    }
  }

  return App;
};
export default App;

2.然后再BasicLayout上添加@App注解

All 30 comments

你可以在 src/layouts/BasicLayout.js 中修改 getMenuData 的来源,不从config中获取,使用动态的数据,但要注意:

  • 数据结构要保持和原来的一致,不然 SiderMenu 无法识别。或者你直接不用 SiderMenu,自己构造侧边栏。
  • 如果 router 还是使用 config 中的数据,需要自己配置每个页面的 name,保证面包屑正常(原来的name是从menu中获取)

使用高阶组件 把BasicLayout 包一层 请求数据 然后传下来就可以了 然后 跟着 @ddcat1115

能给个示例代码吗,各位大神

1.先创建高阶组件App

import React from 'react';
import {connect} from "dva";
import {Spin} from "antd";

const App = (WrappedComponent) => {
  @connect(state => ({
    global: state.global,
  }))
  class App extends React.Component {
    componentDidMount() {
      this.props.dispatch({
        type: 'global/fetchMenus'
      })
    }
    render() {
      const menus = this.props.global.menus;

      return (
        menus.length === 0 ? <Spin spinning={true}/>:
          <WrappedComponent {...this.props} menus={menus}/>
      )
    }
  }

  return App;
};
export default App;

2.然后再BasicLayout上添加@App注解

大神,可不可以再看一下BasicLayout的代码

// 省略之前所有
@App
export default class BasicLayout extends React.Component {
//省略剩下所有

最后是这样吗?
export default connect(state => ({
currentUser: state.user.currentUser,
collapsed: state.global.collapsed,
fetchingNotices: state.global.fetchingNotices,
notices: state.global.notices,
}))(BasicLayout);

关注此问题。

怎么构建符合要求的菜单??
现在报错:Uncaught (in promise) Error: Cannot find module 'function DynamicComponent() {
//。。。。。。。
const list = [
{
name: '基础管理4444data',
path: 'baseData',
icon: 'check-circle-o',
children: [
{
name: '考试数据管理',
path: 'examData',
model: 'exam',
component: './routes/BaseData/ExamData',
},
{
name: '准考证数据管理',
path: 'licenseData',
model: 'license',
component: './routes/BaseData/LicensesData',
},
],
}];
const arr = list.map((item) => {
item.children.map((obj) => {
obj.component = dynamicWrapper(app, [obj.model], () => import(obj.component));
return obj;
});
return item;
});
const navData = [
{
component: dynamicWrapper(app, ['user', 'login'], () => import('./layouts/BasicLayout')),
layout: 'BasicLayout',
name: '首页', // for breadcrumb
path: '/',
children: list,
}];_

希望可以改造菜单后端获取,前端不需要配置那么复杂,角色这一概念在前端感觉有点多余,应该来自于后端;初步思路->用户登陆后同步从后端读取菜单信息(即使有一个进度条或等待),页面维度权限也可以也可以在页面初始化的时候异步加载。后端控制起来也容易些,前端小白纯属吐槽,期待二期用于用于正式环境。

如果 router 还是使用 config 中的数据,需要自己配置每个页面的 name,保证面包屑正常(原来的name是从menu中获取)

name可以自己配置, 那authority怎么办, 这个必须从后台获取吧

按照
@
teeemooo的方法报错
TypeError: Object(...) is not a function

我参考这个可以实现
https://github.com/ant-design/ant-design-pro/compare/master...denggy:menus?diff=split&name=menus

菜单不能正常展开怎么弄,点击了就还原了,加了defaultOpenKeys 没用

defaultOpenKeys={['hello']}
          defaultSelectedKeys={['hello']}

使用@teeemooo 的方法能实现,除了上述他分享的代码更改,还需要更改SiderMenu的menuData属性,不再使用原来,使用你放到props中的menus(记得formater menus)

有人实现了吗

// 省略之前所有 @App export default class BasicLayout extends React.Component { //省略剩下所有
外面包装一层高阶组件,就是在原有组件代码上一行加个高阶组件@App的注释,这个语法没看懂?哪里有相关文档?
另外:
const App = (WrappedComponent) => { @connect(state => ({ global: state.global, }))
这个语法也没看懂,按理返回的(只嵌套)是App Compnent这个class(函数),这么返回两行过程代码不会报语法错误吗?

额,这两句是不是传给connect高阶函数的两个参数,返回一个做了绑定的组件??

求解释,谢谢。。

@teeemooo

@App是装饰器

@App
class BasicLayout{}

就是相当于

class BasicLayout{}
App(BasicLayout)

const App = (WrappedComponent) => { @connect(state => ({ global: state.global, }))
这个要连着下面一起看

@connect(state => ({ global: state.global}))
class App extends React.Component {
  ........
}

上面那个connect(state => ({ global: state.global}))其实应该也是返回一个装饰器
所以

@connect(state => ({
    global: state.global,
}))
class App extends React.Component {
    .......
}

return App;

意思就是

class App extends React.Component {
    .......
}
return connect(state => ({global: state.global }))(App)

应该是这样的意思,搜一下js装饰器就有了
@FatFatFox

@zhang0ZGC 可否再发一遍链接,上一个链接已失效。感谢。

@JevonYang 我也不知道是啥了。。好像是 #1290 的,你看下那个file chnages

根据#1555

src/components/SiderMenu/SiderMenu.js 组件constructor里初始化了this.menus = props.menuData,直接用props.menuData替换this.menu

SiderMenu组件定义了,this.menus = props.menuData,在组件使用的是this.menu,所以如何改变props的值,组件都不会变化。

如果将this.menu替换成props.menuData,菜单数据可以从后台传过来,但是菜单的展开和收缩会出现问题。是不是可以从这个思路来解决这个问题。

我已经实现了动态菜单渲染 后期我会传到我的GitHub

@guoguanrong123

Can you send the PR with your update?

@veeramarni
https://pro.ant.design/docs/router-and-nav#Menu

Follow this link in version 2.0 if you need to request menu from server-end.

If your project does not require a menu, you can remove the mount of the SiderMenu component directly in BasicLayout. And set const MenuData = [] in src/layouts/BasicLayout.
If you need to request a menu from the server, you can set menuData to state. Then the status is modified by the network acquisition.

@JevonYang I don't see how to generate dynamic menu as explained in https://github.com/ant-design/ant-design-pro/issues/1895

@veeramarni

the author is very lazy ,follow this :

src/layouts/BasicLayout.js :

  getMenuData() {
   # const {
   #   route: { routes },
   # } = this.props;

    //console.log(route) to get router.config's Json Array

    //rewrite it from api with same format before
    const route = request.getRoute();
    return formatter(routes);

   }





@WG-Com
给你一个不推荐的搞法:
image

2,0异步菜单已经是分分钟的事情了
特别是
91be534a97bcc3400a003ccce0168e66368a3440 这次提交之后

This is the way i implemted it.
First i built a Wrapper:

import React from 'react';
import {connect} from "dva";
import {Spin} from "antd";
import { cloneDeep } from 'lodash';

const FetchRoutes = (WrappedComponent) => {
  @connect(state => ({
    global: state.global,
  }))
  class FetchRoutes extends React.Component {
    componentDidMount() {
      this.props.dispatch({
        type: 'global/fetchRoutes'
      })
    }
    render() {
      const routes = this.props.global.routes;
      var route = cloneDeep(this.props.route)
      route.routes =  routes
      return (
        routes.length === 0 ? <Spin spinning={true}/>:
          <WrappedComponent {...this.props} route={route}  />
      )
    }
  }

  return FetchRoutes;
};
export default FetchRoutes;

then i wrapped the components that needed to obtain this information with the Wrapper for instance:

import FetchRoutes from '../wrappers/FetchRoutes';
...
@FetchRoutes
class BasicLayout extends React.PureComponent {.....
Was this page helpful?
0 / 5 - 0 ratings

Related issues

952425340 picture 952425340  ·  3Comments

zhuanglong picture zhuanglong  ·  3Comments

suifan picture suifan  ·  3Comments

renyi818 picture renyi818  ·  3Comments

lvzheng0404 picture lvzheng0404  ·  3Comments