二、React组件化

使用第三方组件

安装: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,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,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 形式,而且要注意是浅比较

相关文章

react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接...
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc ...