render-props和高阶组件
-
React组件复用(联想函数封装)
-
复用什么?1. state 2. 操作state的方法(组件状态逻辑)
-
两种方式:1. render props模式 2. 高阶组件(HOC)
-
注意:这两种方式不是新的API,而是利用React自身特点的编码技巧,演化而成的固定模式(写法)
render props 模式(其实就是子组件给父组件传参)
在使用组件时,添加一个值为函数的prop,通过函数参数获取(需要组件内部实现)
<Mouse render={(mouse)=>{}}/>
<Mouse render={(mouse)=>(<p>鼠标当前位置{mouse.x},{mouse.y}</p>)}/>
class Mouse extends React.Component{ //鼠标位置state state={ x:0, y:0 } //鼠标移动事件的事件程序 handleMouseMove=e=>{ this.setState({ x:e.clientX, y:e.clientY }) } //监听鼠标移动事件 componentDidMount(){ window.addEventListener("mousemove",this.handleMouseMove) } componentWillUnMount(){ window.removeEventListener("mousemove",this.handleMouseMove) } render(){ return this.props.render(this.state) } } class App extends React.Component{ render(){ return ( <div> <h1>render proprs 模式</h1> <Mouse render={(mouse)=><p>鼠标当前位置{mouse.x},{mouse.y}</p>}/> <Mouse render={mouse=>(return <img src={img} alt="猫" style={ { position:"absolute", top:mouse.y, left:mouse.x }}> )}/> </div> ) } } ReactDOM.createRoot("root").render(<App/>)
使用步骤
-
创建Mouse组件,在组件中提供复用的状态逻辑代码(1.状态 2.操作状态的方法)
-
将要复用的状态作为props.render(state)方法的参数,暴露到组件外部
-
使用props.render()的返回值作为要渲染的内容
children代替props属性
-
注意:并不是该模式叫render props就必须使用名为render的prop,实际上可以使用任意名称的prop
-
把prop是一个函数并且告诉组件要渲染什么内容的技术叫做:render props模式
-
推荐:使用children代替render属性
<Mouse> {mouse=><p>鼠标的位置是{mouse.x},{mouse.y}</p>} </Mouse> //组件内部: this.props.children(this.state)
代码优化
1.推荐:给render props模式添加props校验
2.应该在组件卸载时解除mousemove事件绑定
Mouse.propTypes={ children:PropTypes.func.isRequired }
componentWillUnmount(){ window.removeEventListener("mousemove",this.handleMouseMove) }
高阶组件
概述
-
目的:实现状态逻辑复用
-
采用包装(装饰)模式,比如说:手机壳
-
手机:获取保护功能
-
手机壳:提供保护功能
-
高阶组件就相当于手机壳,通过包装组件,增强组件功能
思路分析
-
高阶组件(HOC,Highter-Order Component) 是一个函数,接收要包装的组件,返回增强后的组件
-
高阶组件内部创建一个类组件,在这个类组件中提供复用的状态逻辑代码,通过prop将复用的状态传递给被包装组件WrappedComponent
-
const EnhancedComponent=withHOC(WrappedComponent)
//高阶组件内部创建的类组件 class Mouse extends React.Component{ render(){ return <WrappedComponent {...this.state}/> } }
使用步骤
-
创建一个函数,名称约定以with开头
-
指定函数参数,参数应该以大写字母开头(作为要渲染的组件)
-
在函数内创建一个类组件,提供复用的状态逻辑代码,并返回
-
在该组件中,渲染参数组件,同时将状态通过prop传递给参数组件
-
调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中
function withMouse(WrappedComponent){ class Mouse extends React.Component{} return Mouse }
//Mouse组件的render方法中: return <WrappedComponent {...this.state}/> //创建组件 const MousePosition = withMouse(Position) //渲染组件 <MousePosition/>
//例如 //创建高阶组件 function withMouse(WrappedComponent){ //该组件提供复用的状态逻辑 class Mouse extends React.Component{ //鼠标状态 state={ x:0, y:0 } handleMouseMove = e =>{ this.setState({ x:e.clientX, y:e.clientY }) } //控制鼠标状态的逻辑 componentDidMount(){ window.addEventListener("mousemove",this.handleMouseMove) } componentWillUnmount(){ window.removeEventListener("mousemove",this.handleMouseMove) } render(){ return <WrappedComponent {...this.state}></WrappedComponent> } } return Mouse } /* const Cat=props=>( <img src={img} alt="" style={ { position:"absolute", top:props.y-64 left:props.x-64 }}/> ) */ //用来测试高阶组件 const Position = props =>( <p> 鼠标当前位置:(x:{props.x},y:{props.y}) </p> ) //获取增强后的组件 const MousePosition = withMouse(Position) class App extends React.Component{ render(){ return ( <div> <h1>高阶组件</h1> {/*渲染增强后的组件*/} <MousePosition/> </div> ) } } ReactDOM.createRoot("root").render(<App/>)
设置displayName
-
使用高阶组件存在的问题:得到的两个组件名称
-
原因:默认情况下,React使用组件名称作为displayName
-
解决方式:为高阶组件设置displayName便于调试时区分不同的组件
-
displayName的作用:用于设置调试信息(React Developer Tools信息)
-
设置方式:
-
Mouse.displayName=`WithMouse${getDisplayName(WrappedComponent)}` function getDisplayName(WrappedComponent){ return WrappedComponent.dispalyName || WrappedComponent.name || 'Component'