使用第三方组件
安装:npm install antd --save
试用 ant-design 组件库
import React, { Component } from 'react'
import Button from 'antd/lib/button';
import 'antd/dist/antd.css'
export default class TestAntd extends Component {
render() {
return (
<div>
<Button type="primary">Button</Button>
</div>
)
}
}
上面 import 的内容太长,不利于日常开发
配置按需加载
安装 react-app-rewired 取代 react-scripts,可以扩展webpack的配置,类似 vue.config.js
npm install react-app-rewired customize-cra babel-plugin-import -D
// 根目录创建 config-overrides.js
const { override, fixBabelImports, addDecoratorsLegacy } = require("customize-cra")
module.exports = override(
fixBabelImports("import", {
libraryName: "antd",
libraryDirectory: "es",
style: "css"
}),
addDecoratorsLegacy() // 配置装饰器,这里如果配置,需要先安装下面的npm
)
修改package.json
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
}
支持装饰器配置
npm install -D @babel/plugin-proposal-decorators
例如:高级组件链式调用
import React, { Component } from 'react'
const foo = Cmp => props => {
return <div style={{border: '1px solid red'}}>
<Cmp {...props}/>
</div>
}
const foo2 = Cmp => props => {
return <div style={{border: '1px solid green', padding: '10px'}}>
<Cmp {...props}/>
</div>
}
function Child(props){
return <div>Child</div>
}
export default class HocPage extends Component {
render() {
const Foo = foo2(foo(Child))
return (
<div>
<h1>HocPage</h1>
<Foo />
</div>
)
}
}
改为装饰器的写法:装饰器只能用于装饰 class 组件
import React, { Component } from 'react'
const foo = Cmp => props => {
return <div style={{border: '1px solid red'}}>
<Cmp {...props}/>
</div>
}
const foo2 = Cmp => props => {
return <div style={{border: '1px solid green', padding: '10px'}}>
<Cmp {...props}/>
</div>
}
@foo2
@foo
class Child extends Component {
render(){
return <div>Child</div>
}
}
export default class HocPage extends Component {
render() {
return (
<div>
<h1>HocPage</h1>
<Child />
</div>
)
}
}
注意:需要引入 antd样式文件: import 'antd/dist/antd.css';
使用antd的Form表单
import React, { Component } from 'react'
import {Form, Input, Icon, Button} from 'antd'
const FormItem = Form.Item
export default class FormPage extends Component {
constructor(props){
super(props)
this.state = {
name: '',
password: ''
}
}
change = (field, event)=>{
this.setState({
[field]: event.target.value
})
}
submit = ()=>{
console.log('state', this.state);
}
render() {
return (
<div>
<h1>FormPage</h1>
<Form>
<FormItem label="姓名">
<Input prefix={<Icon type="user"/>} onChange={event => this.change('name', event)}/>
</FormItem>
<FormItem label="密码">
<Input type="password" prefix={<Icon type="lock"/>} onChange={event => this.change('password', event)}/>
</FormItem>
<FormItem>
<Button type="primary" onClick={this.submit}>提交</Button>
</FormItem>
</Form>
</div>
)
}
}
Form中内容的使用 --- Form.create()
import React, {Component} from 'react'
import {Form, Input, Icon, Button} from 'antd'
const FormItem = Form.Item
class FormPageDecorators extends Component {
submit = () => {
const { getFieldsValue, getFieldValue } = this.props.form
// 获取所有Field的值
console.log('submit', getFieldsValue());
// 获取单个值
console.log('submitName', getFieldValue('name'));
}
render() {
// 装饰器
const {getFieldDecorator} = this.props.form
console.log(this.props.form);
return (
<div>
<h1>FormPageDecorators</h1>
<Form>
<FormItem label="姓名">
{ getFieldDecorator('name')(<Input prefix={< Icon type = "user" />}/> )}
</FormItem>
<FormItem label="密码">
{ getFieldDecorator('password')(<Input type="password" prefix={< Icon type = "lock" />}/> )}
</FormItem>
<FormItem>
<Button type="primary" onClick={this.submit}>提交</Button>
</FormItem>
</Form>
</div>
)
}
}
export default Form.create()(FormPageDecorators)
表单验证
// 校验规则
const nameRule = {
required: true,
message: 'please input your name'
}
const passwordRule = {
required: true,
message: 'please input your password'
}
class FormPageDecorators extends Component {
submit = () => {
const { validateFields } = this.props.form
// 表单验证
validateFields((err, values)=>{
if(err){
console.log('err:', err)
}else{
console.log('submit:', values);
}
})
}
}
<FormItem label="姓名">
{ getFieldDecorator('name', {rules: [nameRule]})(<Input prefix={< Icon type = "user" />}/> )}
</FormItem>
<FormItem label="密码">
{ getFieldDecorator('password', {rules: [passwordRule]})(<Input type="password" prefix={< Icon type = "lock" />}/> )}
</FormItem>
自己写一个Form.create()
import React, { Component } from 'react' function kFormCreate(Cmp){ return class extends Component { constructor(props){ super(props) this.options = {} // 配置字段项 this.state = {} // 存字段值 } handleChange = (event)=>{ const {name, value} = event.target this.setState({ [name]: value }) } getFieldDecorator = (field, options)=>{ this.options[field] = options return InputCmp=>( <div className="border"> {React.cloneElement(InputCmp,{ name: field, value: this.state[field] || "", onChange: this.handleChange })} </div> ) } getFieldsValue = ()=>{ return {...this.state} } getFieldValue = (field)=>{ return this.state[field] } validateFields = (callback)=>{ const tem = {...this.state} const err = [] for(let i in this.options){ if(tem[i] === undefined){ err.push({ [i]: 'error' }) } } if(err.length>0){ callback(err, tem) }else{ callback(undefined, tem) } } render(){ return ( <div className="border"> <Cmp {...this.props} getFieldDecorator={this.getFieldDecorator} getFieldsValue={this.getFieldsValue} getFieldValue={this.getFieldValue} validateFields={this.validateFields} /> </div> ) } } } // 校验规则 const nameRule = { required: true, message: 'please input your name' } const passwordRule = { required: true, message: 'please input your password' } class MyFormPage extends Component { submit = ()=>{ const {getFieldsValue, getFieldValue, validateFields} = this.props validateFields((err, values)=>{ if(err){ console.log('err', err); }else{ console.log('submitSuccess:',values); } }) } render() { const {getFieldDecorator} = this.props return ( <div> <h1>MyFormPage</h1> { getFieldDecorator('name',{rules: [nameRule]})( <input type="text"/> ) } { getFieldDecorator('password',{rules: [passwordRule]})( <input type="password"/> ) } <button onClick={this.submit}>提交</button> </div> ) } } export default kFormCreate(MyFormPage)
Dialog组件(弹窗组件)
Dialog.js
import React, { Component } from 'react' import {createPortal} from 'react-dom'; export default class Dialog extends Component { constructor(props){ super(props) const doc = window.document this.node = doc.createElement('div') doc.body.appendChild(this.node) } componentWillUnmount(){ window.document.body.removeChild(this.node) } render() { return createPortal( <div className="dialog"> <h1>Dialog</h1> </div>, this.node ) } }
DialogPage.js
import React, { Component } from 'react' import {Button} from 'antd'; import Dialog from '../components/Dialog'; export default class DialogPage extends Component { constructor(props){ super(props) this.state = { showDialog: false } } handleShowDialog = ()=>{ this.setState({ showDialog: !this.state.showDialog }) } render() { const {showDialog} = this.state return ( <div> <h1>DialogPage</h1> <Button onClick={this.handleShowDialog}>dialog toggle</Button> { showDialog && <Dialog /> } </div> ) } }
树形组件
css文件
.tri { width: 20px; height: 20px; margin-right: 2px; padding-right: 4px; } .tri-close:after, .tri-open:after { content: ""; display: inline-block; width: 0; height: 0; border-top: 6px solid transparent; border-left: 8px solid black; border-bottom: 6px solid transparent; } .tri-open:after { transform: rotate(90deg); }
TreeNode.js
import React, { Component } from 'react' import classnames from 'classnames'; export default class TreeNode extends Component { constructor(props){ super(props) this.state = { expanded: false } } handleExpanded = ()=>{ this.setState({ expanded: !this.state.expanded }) } render() { const {title, children} = this.props.data const {expanded} = this.state const hasChildren = children && children.length > 0 return ( <div> <div className="nodesInner" onClick={this.handleExpanded}> { hasChildren && <i className={classnames("tri", expanded ? 'tri-open':'tri-close')}></i> } <span>{title}</span> </div> { hasChildren && expanded && <div className="children"> { children.map(item=>{ return <TreeNode key={item.key} data={item}/> }) } </div> } </div> ) } }
TreePage.js
import React, { Component } from 'react' import TreeNode from '../components/TreeNode'; //数据源 const treeData = { key: 0, //标识唯⼀一性 title: "全国", //节点名称显示 children: [ //⼦子节点数组 { key: 6, title: "北方区域", children: [{ key: 1, title: "⿊龙江省", children: [{ key: 6, title: "哈尔滨", }, ], }, { key: 2, title: "北京", }, ], }, { key: 3, title: "南方区域", children: [{ key: 4, title: "上海", }, { key: 5, title: "深圳", }, ], }, ], }; export default class TreePage extends Component { render() { return ( <div> <h1> TreePage </h1> <TreeNode data={treeData}/> </div> ) } }
常见组件优化技术
定制组件的 shouldComponentUpdate 钩子
import React, { Component } from 'react' export default class CommentList extends Component { constructor(props){ super(props) this.state = { comments: [] } } componentDidMount(){ setInterval(() => { this.setState({ comments: [{ author: "⼩明", body: "这是小明写的⽂文章", }, { author: "小红", body: "这是小红写的⽂文章", }] }) }, 1000); } render() { const {comments} = this.state return ( <div> <h1>CommentList</h1> { comments.map(item=>{ return <Comment key={item.author} data={item}/> }) } </div> ) } } class Comment extends Component{ shouldComponentUpdate(nextProps, nextState){ const {author, body} = nextProps const {author: nowAuthor, body: nowBody} = nextProps if(author===nowAuthor && body === nowBody) { return false } } render(){ const {author, body} = this.props.data console.log('render'); return <div className="border"> <p>作者: {author}</p> <p>内容: {body}</p> </div> } }
PureComponent
import React, { Component, PureComponent } from 'react' export default class PureConponentPage extends Component { constructor(props){ super(props) this.state = { counter: 0 } } setCounter = ()=>{ this.setState({ counter: 1 }) } render() { const {counter} = this.state return ( <div> <h1>PureConponentPage</h1> <button onClick={this.setCounter}>change</button> <Demo counter={counter}/> </div> ) } } class Demo extends PureComponent{ render(){ const {counter} = this.props console.log('render'); return <div> {counter} </div> } }
缺点是必须要用 class 形式,而且要注意是浅比较