React:组件的分类

2021-04-15  本文已影响0人  走的是前方的路_端的是生活的苦

React专注View层,组件是React的核心原理之一,一个完成的路由应用层是有一个个独立组件拼装而成的。组件亦是react的基础的一部分。
组件的分类
• 高阶组件与渲染属性
• 受控组件和非受控组件
• 托管组件和非托管组件
• 无状态组件和有状态组件
• 展示型组件和容器组件
高阶组件与渲染属性
高阶组件(HOC) 是react中用于重用组件逻辑的高级技术。HOC本身不是ReactAPI的一部分,他们是从react构思本质中浮现出来的一种模式。具体来说,高阶组件是一个函数,能够接受一个组件并返回一个新组价

export default function showMouse(WrapperComponent) {
  return class extends React.Component {
    constructor(props) {
      super(props)
      this.state = {
        x: 0,
        y: 0,
      }
    }
    handleMouseMove(e) {
      this.setState({
        x: e.clientX,
        y: e.clientY,
      })
    }
    render() {
      const newProps = { tag: 'handMouse' }
      return (
        <div onMouseMove={this.handleMouseMove.bind(this)}>
          <WrapperComponent {...this.props} {...newProps} mouse={this.state} />
        </div>
      )
    }
  }
}
//使用
import showMouse from './pages/personal/showMouse/index'
class App extends React.Component {
      constructor(props) {
    super(props)
  }
  render() {
    const { mouse } = this.props
    return (
      <div className="App">
        <p>X:{mouse.x}</p>
        <p>Y:{mouse.y}</p>
      </div>
        )
  }
}
export default showMouse(App)

• 渲染属性(Render Props)
术语Render Props 是指一种技术,用于使用一个值为函数的prop在react组件之间的代码共享
拿上边例子,假设,有一个需求要在鼠标移动时,先界面上显示鼠标坐标,但由于此功能要在多个组件, 多个页面中使用。意味着界面显示数据是固定的,但UI组件显示方式会发生变化。在前端开发中,UI是高频变化的,业务逻辑与业务数据反而是相对稳定,这种将展示数据交由调用者来控制的方式,极大提高了UI的部分高扩展性。
提示:该组件的官名是Render Props,但不一定要通过一个render.props来实现。该组件的核心思想是:由调用者决定如何显示。所以另一种写法如下:

import React, { Component } from 'react'
export default class Mouser extends Component {
  constructor(props) {
    super(props)
    this.state = {
      x: 0,
      y: 0,
    }
  }
  handleMouseMove(e) {
    this.setState({
      x: e.clientX,
      y: e.clientY,
    })
  }
  render() {
    return (
      <div
        style={{ height: '200px' }}
        onMouseMove={this.handleMouseMove.bind(this)}
      >
        {this.props.children(this.state)}
      </div>
    )
  }
}
//使用
render() {
    return (
      <div className="App">
        <Mouser>
          {(mouse) => (
            <div>
              <p>X:{mouse.x}</p>
              <p>Y:{mouse.y}</p>
            </div>
          )}
        </Mouser>
    )
}

React中的this.props.children API也是个函数,因此,此例子中使用this.props.children方法,同样将UI的展示方式交由了调用者决定。
• 高阶组价和渲染属性的特点
• 高阶组件特点
• 接受一个组件,返回一个组件
• 代理props,上述例子中<WrappedComponent/>组件的props完全由<Wrapper/>组件控制
• 低侵入性。showMouse函数组件赋予mouse数据,并没有影响外部组件内部的逻辑
• 拥有反向继承的能力
• 渲染组件特点
• 本身不决定数据展示方式,将数据展示方式交由调用者决定
• 无侵入性。上述高阶组件例子中,由于组件被赋予props.mouse。因此组件在调用时不能在拥有相同的props.mouse,因此说高阶组件是低入侵性,而不是无侵入性。但渲染属性是已回调函数的方式,决定其不会有任何侵入性。
• 共同点
• 均有解决逻辑服用的作用
• 均通过逻辑分离、组件拆分、组合调用的方式,实现代码复用,逻辑复用
• 区别:
• 高阶组件通过柯里化函数实现,渲染属性通过回调函数实现
• 高阶组件通过函数调用、装饰器调用。渲染属性通过回调函数重写实现
受控组件和非受控组件
• 受控组件和非受控组件是针对表单元素而言
受控组价:在HTML中,像text、textarea、select这类表单元素会维持自身状态,并根据用户输入进行更新,但在react中,可变的状态通常保存在组件的状态属性state中,并且只能通过setState方法更新,相应的,react控制的输入表单元素称为“受控组件”

import React, { Component } from 'react'
export default class AccessControl extends Component {
  state = {
    userName: '张三',
  }
  handleChange = (e) => {
    this.setState({
      userName: e.target.value,
    })
  }
  render() {
    const { userName: name } = this.state
    return (
      <div>
        用户名:
        <input type="text" value={name} onChange={this.handleChange} />
      </div>
    )
  }
}

• 其书写方式和普通的react组件书写区别不大
• 此类组件特指表单元素
• 表单元素数据依赖状态
• 受控组件的修改必须使用onchange事件绑定
• 实时映射状态值
• 非受控组件
与受控组件相反,非受控组件不受状态的控制,通过virtual Dom来获取数据

import React, { Component } from 'react'
export default class noControls extends Component {
  constructor(props) {
    super(props)
    this.handleSubmit = this.handleSubmit.bind(this)
    this.input = React.createRef()
  }
  handleSubmit(e) {
    console.log('the value', this.input.value)
    e.preventDefault()
  }
  render() {
    return (
      <form>
        <input type="text" name="names" ref={(input) => (this.input = input)} />
        <input type="submit" value="submit" onClick={this.handleSubmit} />
      </form>
    )
  }
}

• 特指表单元素
• 表单数据存储在DOM中
• 通过virtual dom的方式获取表单数据
托管组件和非托管组件
• 托管组件:将数据委托其他组件控制,下面例子中的inputLeft与inputRight组件本身发不处理用户输入的信息,直接托管到父组件Hosting中,在没有Mobx和redux等状态管理react项目中,通常采用状态提升,把所有数据委托给一个相同的父组件,由父组件控制数据与逻辑,把这种数据托管给其他组件控制的组件,称之为托管组件

import React, { Component } from 'react'
class InputLeft extends Component {
  constructor(props) {
    super(props)
    this.handleChange = this.handleChange.bind(this)
  }
  handleChange(e) {
    const { onChange } = this.props
    onChange && onChange(e.target.value)
  }
  render() {
    return (
      <input
        type="text"
        name="left"
        value={this.props.value}
        onChange={this.handleChange}
      />
    )
  }
}
class InputRight extends Component {
  constructor(props) {
    super(props)
    this.handleChange = this.handleChange.bind(this)
  }
  handleChange(e) {
    const { onChange } = this.props
    onChange && onChange(e.target.value)
  }
  render() {
    return (
      <input
        type="text"
        name="right"
        value={this.props.value2}
        onChange={this.handleChange}
      />
    )
  }
}
export default class Hosting extends Component {
  constructor(props) {
    super(props)
    this.state = {
      value: '',
      value2: '',
    }
    this.handleChange = this.handleChange.bind(this)
  }
  handleChange(value) {
    this.setState({
      value: value + '',
    })
    console.log('value is', this.state.value)
  }
  render() {
    const { value } = this.state
    const { value2 } = this.state
    return (
      <div>
        LEFT:
        <InputLeft value={value} onChange={this.handleChange}></InputLeft>
        RIGHT:
        <InputRight value={value2} onChange={this.handleChange}></InputRight>
      </div>
    )
  }
}

• 非托管组件:及组件通过拥有状态处理自身的数据

import React ,{Component} from 'react'
export default class BaseInput extends Component{
    constructor(){
    this.satet = {
        name:''
    }
  }
  handleChange(e){
    const name = e.target.value;
    this.setState({name})
  }
  render(){
    return(
        <input type="text" value={this.state.name} onChange={this.handleChange.bind(this)} />
    )
  }
}

展示型组件和容器组件
• 容器型组件:主要是组件是如何工作的,更具体的就是数据是怎么更新的,不包含任何的virtual dom(虚拟dom)的修改和组合,只关心数据的更新,不在乎dom是什么样的

import React from 'react'
import ReactDom from 'react-dom'
import RootTable from './RootTable' //引入展示型组件
class RootSearch extends React.Component{
  constructor(props){
    this.state = {id:'',name:'',nikeName:''}
  }
  componentDidMount(){
    this.axios.get('/user',{params:{id:2123}}).then((res)=>{
        this.setState({
        id:res.data.id,
        name:res.data.name,
        nikeName:res.data.nikeName
      })
    })
  }
    render(
    <div>
        <RootTable id={this.state.id} name={this.state.name} />
     </div>
  )
}

• 展示型组件:其主要的就是组件要怎么渲染,对virtual Dom具体是怎么操作的,也包含样式的修改组合,同时,它不依赖任何形式的store

const UIcomponent = (props) =(
    <div>
    {props.dataSource.map(callbackfn:item=>(
        <p onClick={()=>props.onClick(item)} >item.userName</p>
     ))}
  </div>
)

对此,展示型组件只考虑两个事情:输入,输出
无状态组件和有状态组件
• 无状态组件:就是没有私有状态的组件,没有state
• 无状态组件目前有两种写法:

//无状态 类组件 stateless class component(scc)
class sccComponenet extend React.Componenet{
  render(){
    return(
      <div>用户名:
        <input type="text" value={this.props.value} onChange={this.props.onChange} />
      </div>
    )
  }
}
//无状态 函数组件 stateless Function Component(SFC)
const SFCcomponent = (props)=>{
    <div>用户名:
        <input type="text" value={props.value} onChange={props.onChange} />
      </div>
}

export default SFCcomponent
这两种写法的区别:
• 在SCC中仍然可以访问react的生命周期,如componentDidMount等
• 在SFC中则无法访问
• 所以SFC在渲染上高于SCC
• 状态组件:带有state用以处理业务逻辑、UI逻辑、数据处理等。配合react的生命周期函数,用以在特殊时刻控制状态

class StatefulComponent extends Component{
    constructor(props){
    super(props)
    this.state ={//定义状态}
  }
  componentDidMount(){//do something}
}
上一篇下一篇

猜你喜欢

热点阅读