【React -- 5/100】 组件复用
组件复用
React组件复用概述
- 思考:如果两个组件中的部分功能相似或相同,该如何处理?
- 处理方式:复用相似的功能
- 复用什么?
- state
- 操作state的方法
- 两种方式:
- render props模式
- 高阶组件(HOC)
- 注意: 这两种方式不是新的API,而是利用React自身特点的编码技巧,演化而成的固定模式
1- render-props模式
- 思路:将要复用的state和操作state的方法封装到一个组件中
- 如何拿到该组件中复用的state
- 在使用组件时,添加一个值为函数的prop,通过函数参数来获取
<Component render={(props) =>{}} />
- 如何渲染到任意的UI
- 使用该函数的返回值作为要渲染的UI内容
<Component render={(props) =>
<p>{props.attributeA} --- {props.attributeB}</p>
} />
使用步骤
- 创建Mouse组件,在组件中提供复用的逻辑代码
- 将要复用的状态作为 props.render(state)方法的参数,暴露到组件外部
- 使用props.render() 的返回值作为要渲染的内容
children代替render属性
- 注意:并不是该模式叫 render props就必须使用名为render的prop,实际上可以使用任意名称的prop
- 把prop是一个函数并且告诉组件要渲染什么内容的技术叫做: render props模式
- 推荐:使用childre代替render属性
优化代码
推荐给render props模式添加props校验
移除鼠标事件的监听
// 添加校验规则
Mouse.propTypes = {
children: PropTypes.func.isRequired
};// 在组件卸载时移除事件绑定
componentWillUnmount() {
window.removeEventListener('mousemove', this.handleMousMove)
}
code
// 导入图片资源
import img from './res/img/9d82d158ccbf6c814204fcabbf3eb13533fa4046.gif'
/* render props 模式*/
// 创建mouse组件
class Mouse extends React.Component {
// 鼠标位置
state = {
x: 0,
y: 0
};
handleMousMove = (e) =>{
console.log(e);
this.setState({
x: e.clientX,
y: e.clientY
})
};
// 监听鼠标移动事件
componentDidMount() {
window.addEventListener('mousemove', this.handleMousMove)
}
// 在组件卸载时移除事件绑定
componentWillUnmount() {
window.removeEventListener('mousemove', this.handleMousMove)
}
render() {
return this.props.children(this.state)
}
}
// 添加校验规则
Mouse.propTypes = {
children: PropTypes.func.isRequired
};
class Show extends React.Component {
render() {
return (
<div>
{/* <Mouse render={(mouse) => {
return <p>鼠标的位置: x: {mouse.x} y:{mouse.y} </p>
}}/>*/}
<h2>render props 模式</h2>
{/* children*/}
<Mouse >
{ (mouse) => {
return <p>use children 鼠标的位置: x: {mouse.x} y:{mouse.y} </p>
}}
</Mouse>
{/* pic 图片跟随鼠标移动 */}
{/*<Mouse render={(mouse) => {
return <img src={img} alt="pic" style={{position: 'absolute', top: mouse.y - 50, left: mouse.x - 80, width: '200px',}} />
}}/>*/}
<Mouse>
{(mouse) => {
return <img src={img} alt="pic" style={{position: 'absolute', top: mouse.y - 50, left: mouse.x - 80, width: '200px',}} />
}}
</Mouse>
</div>
)
}
}
ReactDOM.render(<Show/>, document.getElementById('root'));
2 - 高阶组件 (★★★)
目标
- 知道高阶组件的作用
- 能够说出高阶的使用步骤
概述
- 目的:实现状态逻辑复用
- 采用 包装模式
- 手机:获取保护功能
- 手机壳:提供保护功能
- 高阶组件就相当于手机壳,通过包装组件,增强组件功能
思路分析
- 高阶组件(HOC、Higher-Order Component) 是一个函数,接收要包装的组件,返回增强后的组件
const EnhancedComponent = withHOC(WrappedComponent) - 高阶组件内部创建了一个类组件,在这个类组件中提供复用的状态逻辑代码,通过prop将复用的状态传递给被包装组件WrappedComponent
// 高阶组件内部创建的类组件
class Mouse extends React.Component {
render() {
return (
<EnhancedComponent {...this.state} />
)
}
}
使用步骤
创建一个函数,名称约定以with开头
指定函数参数,参数应该以大写字母开头
在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回
function withMouse(WrappedComponent){
class Mouse extends React.Component {}
return Mouse
}在该组件中,渲染参数组件,同时将状态通过prop传递给参数组件
调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面
/* 高阶组件*/
// 导入图片资源
import img from './res/img/9d82d158ccbf6c814204fcabbf3eb13533fa4046.gif'
// 创建高阶组件
function withMouse(WrappedComponent){
// 该组件提供复用的状态逻辑
class Mouse extends React.Component {
// 鼠标状态
state = {
x: 0,
y: 0
}
// 控制鼠标状态的逻辑
handleMouseMove = (e) =>{
// console.log(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} {...this.props}/>
}
} // 设置diaplayName
Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`;
return Mouse
}
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}// 测试组件
const Position = props => {
return鼠标的位置: x: {props.x} y:{props.y}
};
const PicMove = props => (
<img src={img} alt="pic" style={{position: 'absolute', top: props.y - 50, left: props.x - 80, width: '200px',}} />
);// 获取增强后的组件
const MousePosition = withMouse(Position);// 图片跟随鼠标移动
const PicPosition = withMouse(PicMove);class Show extends React.Component {
render() {
return (高阶组件
{/* 渲染高阶组件*/}
)
}
}
ReactDOM.render(, document.getElementById('root'));
设置displayName
使用高阶组件存在的问题:得到两个组件的名称相同(React默认使用组件的名称为displayname)
原因:默认情况下,React使用组件名称作为displayName
解决方式:为高阶组件设置displayName,便于调试时区分不同的组件
displayName的作用:用于设置调试信息(React Developer Tools信息)
设置方式:
// 设置diaplayName
Mouse.displayName =WithMouse${getDisplayName(WrappedComponent)}
;function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}
传递props
问题:如果没有传递props,会导致props丢失问题
解决方式: 渲染WrappedComponent时,将state和props一起传递给组件
render() {
return <WrappedComponent {...this.state} {...this.props}/>
}
React 组件进阶:
- 组件通讯是构建React应用必不可少的一环
- props的灵活性让组件更加强大
- 状态提升是React组件的常用模式
- 组件生命周期有助于理解组件的运行过程
- 钩子函数让开发者可以在特定的时机执行某些功能
- render props 模式和高阶组件都可以实现组件状态逻辑的复用
- 组件极简模型: (state,props) => UI
【React -- 5/100】 组件复用的更多相关文章
- React组件复用的方式
React组件复用的方式 现前端的工程化越发重要,虽然使用Ctrl+C与Ctrl+V同样能够完成需求,但是一旦面临修改那就是一项庞大的任务,于是减少代码的拷贝,增加封装复用能力,实现可维护.可复用的代 ...
- React -- 3/100 】组件通讯
通讯 | props | prop-types 组件通讯 Props: 组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props /* class */ .parent-box ...
- React jQuery公用组件开发模式及实现
目前较为流行的react确实有很多优点,例如虚拟dom,单向数据流状态机的思想.还有可复用组件化的思想等等.加上搭配jsx语法和es6,适应之后开发确实快捷很多,值得大家去一试.其实组件化的思想一直在 ...
- 函数式编程与React高阶组件
相信不少看过一些框架或者是类库的人都有印象,一个函数叫什么creator或者是什么什么createToFuntion,总是接收一个函数,来返回另一个函数.这是一个高阶函数,它可以接收函数可以当参数,也 ...
- React高阶组件 和 Render Props
高阶组件 本质 本质是函数,将组件作为接收参数,返回一个新的组件.HOC本身不是React API,是一种基于React组合的特而形成的设计模式. 解决的问题(作用) 一句话概括:功能的复用,减少代码 ...
- Svelte入门——Web Components实现跨框架组件复用
Svelte 是构建 Web 应用程序的一种新方法,推出后一直不温不火,没有继Angular.React和VUE成为第四大框架,但也没有失去热度,无人问津.造成这种情况很重要的一个原因是,Svelte ...
- React Native 之 组件化开发
前言 学习本系列内容需要具备一定 HTML 开发基础,没有基础的朋友可以先转至 HTML快速入门(一) 学习 本人接触 React Native 时间并不是特别长,所以对其中的内容和性质了解可能会有所 ...
- React Native的组件ListView
React Native的组件ListView类似于iOS中的UITableView和UICollectionView,也就是说React Native的组件ListView既可以实现UITableV ...
- 聊聊React高阶组件(Higher-Order Components)
使用 react已经有不短的时间了,最近看到关于 react高阶组件的一篇文章,看了之后顿时眼前一亮,对于我这种还在新手村晃荡.一切朝着打怪升级看齐的小喽啰来说,像这种难度不是太高同时门槛也不是那么低 ...
随机推荐
- Codeforces 1203F1 Complete the Projects (easy version)
cf题面 Time limit 2000 ms Memory limit 262144 kB 解题思路 看见这题觉得贪心可做,那就贪吧.(昨天真是贪心的一天,凌晨才被这两道贪心题虐,下午多校又来,感觉 ...
- python3.7--pycharm selenium自启360浏览器/360极速浏览器方法
写于:2019.01.02(实测日) 参考文档:https://blog.csdn.net/five3/article/details/50013159 一.下载360浏览器或360极速浏览器的Chr ...
- Java九种基本数据类型,以及他们的封装类
基本类型 大小(字节) 默认值 封装类 byte 1 (byte)0 Byte short 2 (short)0 Short int 4 0 Integer long 8 0L Long float ...
- long poll、ajax轮询和WebSocket
websocket 的认识深刻有木有.所以转到我博客里,分享一下.比较喜欢看这种博客,读起来很轻松,不枯燥,没有布道师的阵仗,纯粹为分享.废话这么多了,最后再赞一个~ WebSocket是出的东西(协 ...
- 第十一周java学习总结
目录 第十一周java学习总结 学习内容 学习总结 提交代码截图 代码推送 第十一周java学习总结 学习内容 第13章 Java网络编程 主要内容 URL类 InetAdress类 套接字 UDP数 ...
- How jQuery UI Works
https://learn.jquery.com/jquery-ui/how-jquery-ui-works/ jQuery UI contains many widgets that maintai ...
- 一个”.java”源文件中是否可以包含多个类(不是内部类)?有什么限制
这个是可以的,一个“.java”源文件里面可以包含多个类,但是只允许有一个public类,并且类名必须和文件名一致. 每个编译单元只能有一个public 类.这么做的意思是,每个编译单元只能有一个公开 ...
- [ros] ros入门记录
ROS入门 半天入门ROS,总体感觉比较好理解,python写不用编译超级爽,学完ros去学电控去了. ros2比ros1好用,所以最终是学ros2. ros1 安装 添加源 > sudo sh ...
- 1. JDK 、 JRE 、JVM有什么区别和联系?
首先,我们分别对这三者进行阐述. JVM :英文名称(Java Virtual Machine),就是我们耳熟能详的 Java 虚拟机.它只认识 xxx.class 这种类型的文件,它能够将 clas ...
- leetcode 287寻找重复数
这道题用STL容器就很好写了,可以用set也可以用map, 用unordered_map的C++代码如下: class Solution { public: int findDuplicate(vec ...