React:组件的分类
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}
}