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.propsclass 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'// 定义contextconst 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'// 在高阶组件中使用contextconst 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.isLoginif (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%);}
