一、组件复用概述
在实际开发中,如果有个功能基本一致,那么如何来进行处理:
就是将两个功能,进行抽离封装成一个公用的组件,来进行使用,来进行复用(联想函数封装)
在封装组件的过程中,需要封装哪些东西:1.state -- 数据 2.操作数据的方法
React 中的解决方案(两种方式):render-props、高阶组件
二、render props 模式
import React from 'react' /** * 1、先创建组件,在组件内部声明 复用的 state 以及操作 state 的方法 * 2、将要复用的数据作为 props.render(state) 方法的参数,暴露到组件外部 */ // 不能使用函数组件,因为函数组件没有自己的私有数据 // 1、先创建组件,在组件内部声明 复用的 state 以及操作 state 的方法 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) } render() { // 2、要复用的数据作为 props.render(state) 方法的参数,暴露到组件外部 // return this.props.render(this.state) return this.props.render() } } class App extends React.Component { render() { return ( <div> {/* 在使用组件时,添加一个值为函数的prop,通过函数传参 */} <Mouse render={(Mouse) => ( // 如何渲染任意的 UI // 使用该函数的返回值作为要渲染的 UI 内容 <div> <p>x:{Mouse.x}</p> <p>y:{Mouse.y}</p> </div> )}/> </div> ) } } export default App
使用 children 替换 render
import React from 'react' import img from '../images/cat.png' // 定义一个复用组件 class Mouse extends React.Component { // 1、定义数据 state = { x: 0, y: 0 } // 2、定义方法 handleMouseMove = e => { this.setState({ x: e.clientX, y: e.clientY }) } componentDidMount() { // 监听鼠标移动 window.addEventListener('mousemove',this.handleMouseMove) } render() { return this.props.children(this.state) } } class App extends React.Component { render() { return( <div> {/* 鼠标移动 */} <Mouse> { mouse => ( <div> <p>x: {mouse.x}</p> <p>y: {mouse.y}</p> </div> ) } </Mouse> {/* 猫捉老鼠 */} <Mouse> { mouse => ( <img src={img} style={{position: 'absolute',top: mouse.y,left: mouse.x}}/> ) } </Mouse> </div> ) } } export default App
三、高阶组件
import React from 'react' import img from '../images/cat.png' /** * 高阶组件说白了就是包裹、嵌套 */ // 1.创建一个函数,名称以 with 开头 // 2.指定函数参数,参数应该以大写字母开头(作为要渲染的组件) // WrapperComponent 这是一个形参,代表是以后要增强的组件 function withMouse(WrapperComponent) { // 3、创建类组件,并返回 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) } // 4、在类组件内部,返回传入的组件,并传递复用的数据 render() { return <WrapperComponent {...this.state} /> } } Mouse.displayName = `with${getDisplayName(WrapperComponent)}` function getDisplayName(WrapperComponent) { return WrapperComponent.displayName || WrapperComponent.name || 'Component' } return Mouse } // 5、写 UI 结构,用来测试高阶组件 const Position = props => { return ( <p> 鼠标当前位置:(x: {props.x}, y: {props.y}) </p> ) } // 猫捉老鼠 const Cat = props => { return ( <img src={img} alt="" style={{ position: 'absolute', top: props.y - 64, left: props.x - 64 }}/> ) } // 6、调用高阶组件,传入要增强的组件,会返回一个增强的组件 const MousePosition = withMouse(Position) const MouseCat = withMouse(Cat) class App extends React.Component { render() { return ( <div> <MousePosition></MousePosition> <MouseCat></MouseCat> </div> ) } } export default App
四、高阶组件添加 displayName
① 使用高阶组件存在的问题:得到的两个组件名称相同
② 原因:默认情况下,React使用组件名称作为 displayName
③ 解决方式:为 高阶组件 设置 displayName 便于调试时区分不同的组件
④ displayName 作用:用于设置调试信息(React Developer Tools信息)
⑤ 添加方式:
Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`
function getDisplayName(WrappedComponent) { return WrappedComponent.displayName || WrappedComponent.name || 'Component' }
import React from 'react' import img from '../images/cat.png' /** * 高阶组件说白了就是包裹、嵌套 */ // 1.创建一个函数,名称以 with 开头 // 2.指定函数参数,参数应该以大写字母开头(作为要渲染的组件) // WrapperComponent 这是一个形参,代表是以后要增强的组件 function withMouse(WrapperComponent) { // 3、创建类组件,并返回 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) } // 4、在类组件内部,返回传入的组件,并传递复用的数据 render() { return <WrapperComponent {...this.state} /> } } Mouse.displayName = `with${getDisplayName(WrapperComponent)}` function getDisplayName(WrapperComponent) { return WrapperComponent.displayName || WrapperComponent.name || 'Component' } return Mouse } // UI 结构,用来测试高阶组件 const Position = props => { return ( <p> 鼠标当前位置:(x: {props.x}, y: {props.y}) </p> ) } // 猫捉老鼠 const Cat = props => { return ( <img src={img} alt="" style={{ position: 'absolute', top: props.y - 64, left: props.x - 64 }}/> ) } // 5、调用高阶组件,传入要增强的组件,会返回一个增强的组件 const MousePosition = withMouse(Position) const MouseCat = withMouse(Cat) class App extends React.Component { render() { return ( <div> <MousePosition></MousePosition> <MouseCat></MouseCat> </div> ) } } export default App