比木白-React

React

List List.Item

原因

最近在搞CBA m站适配时,尝试写了一把JS 基础组件,首当其冲的就是List List.Item列表组件,在我们m站适配中随处可见,其关键点就在于,怎么将某些在List列表组件中的属性,放到Item列表项组件里面,至此就想到了使用HOC高阶组件。

详情

harmony
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//List组件
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import cns from 'classnames';
import Item from './item';

class List extends PureComponent {
static propTypes = {
className: PropTypes.string,
onClick: PropTypes.func
};

static Item = Item;

constructor(props) {
super(props);
}

//Item高阶组件
itemHOC = () => {
const {children, onClick = () => {}} = this.props;
return (children && children.length > 0) ? React.Children.map(children, (child = {}) => {
const {type: Child, props = {}} = child;
if(Child !== Item) {
throw new TypeError('List component must be an Item component inside~');
}
return <Child {...props} onClick={onClick}/>;
}) : null;
}

render() {
const {className = ''} = this.props;
const {itemHOC = () => {}} = this;
return (
<section className={cns(
'list',
className
)}>
{itemHOC()}
</section>
)
}
}

export default List;
harmony
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//Item组件  
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import cns from 'classnames';
import './index.less';

class Item extends PureComponent {
static propTypes = {
className: PropTypes.string,
onClick: PropTypes.func,
value: PropTypes.any
};

constructor(props) {
super(props);
}

render() {
const {
className = '',
value,
onClick = () => {},
children
} = this.props;
return (
<div className={cns('list-item', className)}
onClick={(e) => onClick(value, e)}
>
{children}
</div>
)
}
}

export default Item;
1
2
3
4
5
6
/*Item组件样式*/
.list-item {
padding: 14px 20px;
background-color: #fff;
border-bottom: 1px solid #ddd;
}

这样就完成了List列表组件 List.Item列表项的基础组件的基础搭建,使用起来也是很简单的…

使用

harmony
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import React, {PureComponent} from 'react';
import List from './components';

const Item = List.Item;

class Demo extends PureComponent {
constructor(props) {
super(props);
}

render() {
const list = Array.from(Array.apply(null, {length: 10}), (item, index) => {
return {[index]: (index + 1)};
});
return (
//...
<List className='demo-list'
onClick={(item, e) => {
console.log(item);
}}
>
{
list && list.length > 0 && list.map((listItem, index) => {
return <Item className='demo-list-item'
value={listItem}
key={index}
>
{JSON.stringify(listItem)}
</Item>
})
}
</List>
)
}
}

react-router路由多层嵌套

原因

最近在搞拓海官网的后台管理时,使用了react-router 4的路由多层嵌套配合继承、装饰器搞了一套路由页面,自我感觉还是不错的,所以我分享这一套嵌套路由配置。

PS: 此页面只适用于嵌套路由,嵌套路由指的是在某一父路由下的子路由只能在此父路由下使用,其余页面不可使用。

使用

首先当然是安装依赖,配置打包、编译、缓存、压缩、分割chunks入口、404重定向、开发环境热更新、生产环境抽取、本地Node服务器和多核处理等等这些,这些属于搭建前端项目生态部分,我在之前的博客当中分享过的’多种方式搭建前端项目’中提到过了,下面我们就直接上干货好了。

harmony
1
2
3
4
5
6
//入口
import React from 'react';
import {render} from 'react-dom';
import Layouts from './layouts';

render(<Layouts />, document.querySelector('#root'));
harmony
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
//基类,用于嵌套路由的继承,以及各级路由承接使用
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {Route} from 'react-router-dom';
import DocumentTitle from 'react-document-title';

class BaseLayout extends PureComponent {
constructor(props) {super(props);}
static propTypes = {
//路由列表
routers: PropTypes.array.isRequired
};


state = {
//扁平化处理之后的路由
routersTransform: []
};

/**
对路由进行扁平化处理
*/
transformRouters = (routers = [], path = '/') => {
const {transformRouters = () => {}} = this;
let {routersTransform = []} = this.state;
routers.forEach((routerItem, routerIndex) => {
routerItem.path = `${path}/${routerItem.path}`.replace(/[\/]+/g, '/');
routersTransform = [...routersTransform, routerItem];
routerItem.children && routerItem.children.length > 0 && transformRouters(routerItem.children, routerItem.path);
});
return routersTransform;
};

/**
查找当前页面路径,并返回地址
*/
searchMatch = (routers = [], pathname = '/') => {
const {searchMatch = () => {}} = this;
for(let item of routers) {
if(item.path.includes(pathname)) {
return item.title;
}
return item.children && item.children.length > 0 && searchMatch(item.children, pathname);
}
};

/**
获取页面的标题
*/
getPageTitle = (props) => {
const {routers = []} = this.props;
const {history: {location: {pathname = ''}}} = props;
const {searchMatch = () => {}} = this;
return searchMatch(routers, pathname);
};

render() {
const {routers = []} = this.props;
//这里用于不嵌套路由时使用
// _routers = [];
// const {transformRouters = () => {}} = this;
// _routers = transformRouters([...routers]);
// return _routers && _routers.length > 0 && _routers.map(routerItem => {
return routers && routers.length > 0 && routers.map(routerItem => {
return <Route
key={routerItem.key}
path={routerItem.path}
exact={routerItem.exact}
component={(props)=> {
const Component = routerItem.component;
return <DocumentTitle>
<Component {...props} routers={routerItem.children} />
</DocumentTitle>
}}
/>
})
}
}

export default BaseLayout;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import Home from '../pages/home';
import Master from '../pages/master';
//路由静态配置
const routers = [{
key: 'home',
path: '/',
title: '拓海官网 - 首页',
name: '首页',
component: Home
}, {
key: 'master',
path: '/master',
title: '拓海官网 - 主子',
name: '主子',
component: Master
}];

export {
routers
}
harmony
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import React from 'react';
import PropTypes from 'prop-types';
import {Layout} from 'antd';
import {BrowserRouter} from 'react-router-dom';
import BaseLayout from './base';
import Headers from './header';
import routers from '../routers';
import * as utils from '../utils';

const {Header, Content} = Layout;

//入口处,使用class继承方式
//装饰器导入路由配置
@utils.packageApi.decorators.getRouters({routers})
class Layouts extends BaseLayout {
constructor(props) {super(props);}

static propTypes = {
//路由列表
routers: PropTypes.array.isRequired
};

render() {
let {routers = []} = this.props;
//这里用于不嵌套路由时使用
// _routers = [];
// const {transformRouters = () => {}} = this;
// _routers = transformRouters([...routers]);
return (
<Layout>
<Header>
{/*<Headers routers={_routers} />*/}
<Headers routers={routers} />
</Header>
<Content>
<BrowserRouter>
{super.render()}
</BrowserRouter>
</Content>
</Layout>
)
}
}

export default Layouts;
harmony
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import React, {PureComponent} from 'react';
//decorators 装饰器
//高阶函数 属性代理
function getRouters(params) {
return (WrapperComponent) => {
return class extends PureComponent {
constructor(props) {super(props);}
state = {
innerParams: {}
};

componentDidMount() {
this.setState({
innerParams: {...params}
});
}

render() {
const {innerParams} = this.state;
return (
<WrapperComponent {...innerParams} />
);
}
}
}
}

export {
getRouters
}
harmony
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {Menu} from 'antd';
import {NavLink} from 'react-router-dom';

const {Item} = Menu;

//头部路由导航
class Headers extends PureComponent {
constructor(props) {super(props);}

static propTypes = {
//路由列表
routers: PropTypes.array.isRequired
};

render() {
const {routers = []} = this.props;
return (
<Menu>
{routers && routers.length > 0 && routers.map((routerItem) => {
return (<Item key={routerItem.key}>
<NavLink to={routerItem.path}>
{routerItem.name}
</NavLink>
</Item>)
})}
</Menu>
)
}
}

export default Headers;

自此react-router路由多层嵌套配置完成,如果想要只在某一路由下使用子路由,重复第2、4、6步即可。

PS:附上我的目录结构图