1、认识高阶组件
高阶组件的本质是一个函数,它接收一个组件作为参数,返回一个新的组件,这就是高阶组件。 简单的理解:就是将一个组件进行相对于复杂的处理、增强之后,返回一个新的组件,我们在项目中就可以使用这个新的组件。
高阶组件的简单使用:
import React, { PureComponent } from 'react'
// 定义一个组件
class App extends PureComponent {
render() {
console.log(this.props);
return (
<div>
<h2>App组件--{ this.props.name }</h2>
</div>
)
}
}
// 给组件取一个别名 好玩
App.displayName = "weiweihu"
// 定义一个函数 实际上就是我们说的高阶组件 接收一个组件作为参数 返回一个新的组件
function enhanceComponent(WrappedComponent) {
// 定义一个组件this.props
class NewComponent extends PureComponent {
render() {
console.log('组件传的值', this.props);
// 将组件传递的值 向下进行传递
return <WrappedComponent { ...this.props }/>
}
}
// 给返回的组件一个别名
NewComponent.displayName = "coder"
// 将组件返回
return NewComponent
}
const EnhanceComponent = enhanceComponent(App)
// 将新组件进行导出
export default EnhanceComponent
// 使用导出的组件 并对组件进行传值
import EnhanceComponent from './EnhanceComponent'
// 使用组件
ReactDOM.render(<EnhanceComponent name="coderweiwei"/>, document.getElementById('root'))
小知识: 我们声明或者定义的组件可以重新起一个展示名(别名)。语法: App.displayName = “NewApp” 不管是类组件还是函数式组件 都是可以起一个展示的名字的
类组件进行定义的时候,写成类的表达式的时候,可以省略类名
class App extends Component {}
// 等价于下面的语句 是同样的效果
const App = class extends Component {}
// 和函数的定义是一样的
function sum() {}
=> 等价于
const sum = function() {}
=> 等价于
const sum = () => {}
2、高阶组件的使用-props的增强
在使用组件的时候,如果我们需要给组件统一增加一个属性props,那么我们可以使用高阶组件来实现这一效果,就是高阶组件拿到我们传入的组件,然后在组件返回的时候,向组件中插入新的属性,那么我们新的组件就拥有新的props了,在组件中就可以直接进行使用了。
import React, { PureComponent } from 'react'
class Home extends PureComponent {
render() {
return (
<div style={{ backgroundColor: 'pink' }}>
<h2>home组件</h2>
<h2>姓名:{ this.props.name }</h2>
<h2>年龄:{ this.props.age }</h2>
<h2>国籍:{ this.props.region }</h2>
</div>
)
}
}
class About extends PureComponent {
render() {
return (
<div style={{ backgroundColor: 'orange' }}>
<h2>about组件</h2>
<h2>姓名:{ this.props.name }</h2>
<h2>年龄:{ this.props.age }</h2>
<h2>国籍:{ this.props.region }</h2>
</div>
)
}
}
// 高阶函数 将所有的组件新增加一个gender属性
// 方法1 类组件的写法
// function enhanceComponentProps(WrappedComponent) {
// class NewComponent extends PureComponent {
// render() {
// // 向传入的组件新增加一个gender属性
// return <WrappedComponent {...this.props} region="中国"/>
// }
// }
// // 将组件进行返回
// return NewComponent
// }
// // 将我们需要增强的组件 仅从统一的处理
// const EnhanceHome = enhanceComponentProps(Home)
// const EnhanceAbout = enhanceComponentProps(About)
// 方法2 函数式组件的写法
// function enhanceComponentProps2(WrappedComponent) {
// function NewComponent(props) {
// return <WrappedComponent {...props} region="中国"/>
// }
// return NewComponent
// }
// 上述组件的简写形式
function enhanceComponentProps2(WrappedComponent) {
// 统一给传入的组件新添加一个region属性
return props => <WrappedComponent {...props} region="中国"/>
}
// 统一对组件进行处理
const EnhanceHome = enhanceComponentProps2(Home)
const EnhanceAbout = enhanceComponentProps2(About)
export default class App extends PureComponent {
render() {
return (
<div>
<EnhanceHome name="coderweiwei" age={18}/>
<EnhanceAbout name="kobe" age={40}/>
</div>
)
}
}
个人理解:高阶组件就是一个普通的函数,当传入一个组件的时候,我们需要将这个传入的组件进行相应的处理,增加属性、修改数据、修改页面结构,当修改完毕后,再将此组件进行返回。那么我们得到的就是一个经高阶组件处理的新的组件。新的组件拥有新的状态、数据。
3、Context多种使用方式的复习
import React, { PureComponent } from 'react'
// 定义context
const UserInfoContext = React.createContext({
name: 'coderweiwei',
age: 12,
region: '中国'
})
class Home extends PureComponent {
render() {
return (
<div style={{ backgroundColor: 'pink' }}>
<h2>home组件</h2>
<h2>姓名:{ this.context.name }</h2>
<h2>年龄:{ this.context.age }</h2>
<h2>国籍:{ this.context.region }</h2>
</div>
)
}
}
// 方法1 订阅context 订阅之后 可以在组件中直接使用this.context进行访问
Home.contextType = UserInfoContext
// 方法2 在组件内部进行context内容的分发
class About extends PureComponent {
render() {
return (
<UserInfoContext.Consumer>
{
userInfo => {
return (
<div style={{ backgroundColor: 'orange' }}>
<h2>about组件</h2>
<h2>姓名:{ userInfo.name }</h2>
<h2>年龄:{ userInfo.age }</h2>
<h2>国籍:{ userInfo.region }</h2>
</div>
)
}
}
</UserInfoContext.Consumer>
)
}
}
export default class App extends PureComponent {
render() {
return (
<div>
<UserInfoContext.Provider value={{ name: 'weiwei', age: 123, region: '中国' }}>
<Home/>
<About/>
</UserInfoContext.Provider>
</div>
)
}
}
4、高阶组件与Context搭配使用
import React, { PureComponent } from 'react'
// 在高阶组件中使用context
const UserInfoContext = React.createContext({
name: 'coderweiwei',
age: 12,
region: '中国'
})
class Home extends PureComponent {
render() {
return (
<div style={{ backgroundColor: 'pink' }}>
<h2>home组件</h2>
<h2>姓名:{ this.props.name }</h2>
<h2>年龄:{ this.props.age }</h2>
<h2>国籍:{ this.props.region }</h2>
</div>
)
}
}
class About extends PureComponent {
render() {
return (
<div style={{ backgroundColor: 'orange' }}>
<h2>about组件</h2>
<h2>姓名:{ this.props.name }</h2>
<h2>年龄:{ this.props.age }</h2>
<h2>国籍:{ this.props.region }</h2>
</div>
)
}
}
// 高阶组件 组件都是消费者 可将context里面的数据 传入到新的组件 新的组件处理数据后 并将新的组件返回
function enhanceContentToComponent(WrappedComponent) {
return props => {
return (
<UserInfoContext.Consumer>
{
userInfo => <WrappedComponent {...props} {...userInfo}/>
}
</UserInfoContext.Consumer>
)
}
}
// 将Home组件和About组件进行统一处理
const EnhanceHome = enhanceContentToComponent(Home)
const EnhanceAbout = enhanceContentToComponent(About)
export default class App extends PureComponent {
render() {
return (
<div>
<UserInfoContext.Provider value={{ name: '伟伟', age: 123456, region: '中国' }}>
<EnhanceHome/>
<EnhanceAbout/>
</UserInfoContext.Provider>
</div>
)
}
}
5、高阶组件的使用-鉴权操作
鉴权:我们在进入某个页面的之前会对访问者的权限进行控制,比如说没有登录之前是不能访问个人中心页面和购物车页面的,只有当我们登录了,才有访问某些页面的权限。 原理:利用高阶组件对需要鉴权的页面进行统一处理,判断当前页面是否有权限,利用高阶组件返回不同的新组件,如没有登录的话,就返回需要进行登录的界面。如果已经登录的话,就可以返回原始的界面。
import React, { PureComponent } from 'react'
// 在进入某些页面的时候 需要对当前的信息 进行鉴定 有没有登录
// 登录了才会展示例如个人信息页面 没有登录的话 就会跳转到需要登录的页面
class Login extends PureComponent {
render() {
return (
<div>
<h2>欢迎来到登录界面</h2>
</div>
)
}
}
// 需要鉴权的组件
class Cart extends PureComponent {
render() {
return (
<div>
<h2>购物车中心,这里是你的天堂</h2>
</div>
)
}
}
// 高阶组件
function withAuth(WrappedComponent) {
return props => {
// 需要对传递的数据 做相应的判断
const isLogin = props.isLogin
if (isLogin) {
return <WrappedComponent {...props}/>
} else {
return <Login/>
}
}
}
// 增强后的组件
const EnhanceCart = withAuth(Cart)
export default class App extends PureComponent {
render() {
return (
<div>
<p>欢饮回到主页</p>
<EnhanceCart isLogin={ true }/>
</div>
)
}
}
6、高阶组件的使用-劫持生命周期
在高阶函数中,我们定义类组件的时候,类组件具有相应的生命周期,我们可以通过组件的生命周期做一些相应的计算操作,可以在生命周期中函数中执行相应的操作。最后,返回新的组件。
import React, { PureComponent } from 'react'
// 需求: 获取组件渲染的事件
class Home extends PureComponent {
render() {
return (
<div>
<p>home组件</p>
</div>
)
}
}
class About extends PureComponent {
render() {
return <h2>我是about组件</h2>
}
}
// 高阶组件
function withRenderTime(WrappedComponent) {
class HeigerOrderCpn extends PureComponent {
// 开始渲染的时间
UNSAFE_componentWillMount() {
this.startTime = Date.now()
}
// 渲染结束的时间
componentDidMount() {
this.endTime = Date.now()
const time = this.endTime - this.startTime
// 每个组件都有name属性
console.log(`${WrappedComponent.name}组件渲染时间`, time);
}
// 渲染函数
render() {
return <WrappedComponent/>
}
}
// 给高阶组件一个展示名
HeigerOrderCpn.displayName = "Cpn"
// 将组件返回
return HeigerOrderCpn
}
// 将组件高阶化
const EnhanceHome = withRenderTime(Home)
const EnhanceAbout = withRenderTime(About)
export default class App extends PureComponent {
render() {
return (
<div>
app
<EnhanceHome />
<EnhanceAbout />
</div>
)
}
}
上面的案例: 展示的是在组件渲染的时候,我们来计算每个组件的渲染时间,那么单独的在每个组件的生命周期函数中写,代码会显得非常得冗余。如果在高阶组件中,我们得高阶组件可以劫持我们组件的生命周期函数,在组件进行渲染的时候,都会调用组件的生命周期,这样简化的代码,达到了我们想要的效果。其实就是统一的对所有组件的处理。类似于vue中的mixin(混入)。
7、在函数式组件中使用ref
原理:在函数式组件中,因为函数式组件没有生命周期,没有组件的实例对象。所以我们无法像类组件一样获取直接通过ref来获取组件的dom结构。我们使用react内部封装的高阶组件forwardRef函数类对ref绑定dom结构进行转发,转发后,ref可绑定任意组件的dom结构。
import React, { PureComponent, createRef, forwardRef } from 'react'
// 使用ref来访问类组件的实例
class Home extends PureComponent {
render() {
return (
<div>
<p>Home组件</p>
</div>
)
}
}
// 使用ref来访问函数式组件 因为函数式组件没有生命周期函数 没有this实例对象
// 需要需要使用react内部封装的 高阶组件 forwardRef()
const About = forwardRef((props, ref) => {
// props代表的式传递的参数 ref绑定的是相应的dom结构
// console.log(props, ref);
return (
<div ref={ ref }>
<h2>测试数据</h2>
</div>
)
})
export default class App extends PureComponent {
constructor() {
super()
this.homeRef = createRef()
this.aboutRef = createRef()
}
render() {
return (
<div>
<p>app组件</p>
<button onClick={ () => this.btnClick() }>测试按钮</button>
<Home ref={ this.homeRef }/>
<hr/>
<p>可以像类组件一样 给这个组件绑定ref ref经过高阶函数的处理
可以将ref进行转发 转发到函数组件的dom元素上
</p>
<About name='123456' ref={ this.aboutRef }/>
</div>
)
}
btnClick() {
// 可以直接访问 类组件的实例
console.log(this.homeRef.current);
console.log(this.aboutRef.current);
}
}
8、portals的使用
简单的理解就是:控制组件或者元素渲染的具体位置。
import React, { PureComponent } from 'react'
import ReactDOM from 'react-dom'
// 普通的组件
class Home extends PureComponent {
render() {
return (
<div>
<p>Home组件</p>
</div>
)
}
}
// 弹框组件 封装的简单的弹框组件
class Modal extends PureComponent {
// 这里的渲染方法是不一样的 需要使用reactDOM函数库提供的方法
// ReactDOM.createPortal方法的第一个参数是需要渲染的dom结构,第二个参数是需要挂载的dom节点
render() {
return ReactDOM.createPortal(
this.props.children,
document.getElementById('modal')
)
}
}
export default class App extends PureComponent {
constructor() {
super()
this.state = {
show: true
}
}
render() {
return (
<div>
app组件
<button onClick={ () => this.btnClick() }>显示与隐藏</button>
<Home />
{ this.state.show ? <Modal><h2>标题</h2><p>内容1</p><p>内容2</p></Modal> : null }
</div>
)
}
btnClick() {
console.log(this);
this.setState({
show: !this.state.show
})
}
}
# 需要在index.html中创建dom挂载的节点 以及为该节点书写css样式
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!-- 弹框组件挂载的位置 我们自己新创建的 -->
<div id="modal"></div>
</body>
// css样式 是组件水平垂直居中 使用定位进行处理
#modal {
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}