0%

ReactDay4—AdvanceComponent

高阶组件

Higher-order Components(HOC)

高阶组件是以参数为组件,返回值为新组件的函数

本质上对组件进行一次拦截,然后进行处理,返回一个处理后的新组件

缺陷:

需要再原组件上进行包裹或者嵌套,如果大量使用HOC,将会产生非常多的嵌套,导致调试困难

会劫持props,在不遵守约定的情况下会产生冲突

场景 1:props

可以对函数组件和类组件进行处理.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import React, { PureComponent } from 'react'


function enhancedUser(OriginComponent) {
class NewComponent extends PureComponent {
constructor(props) {
super(props)

this.state = {
userInfo : {
name: "codewhy",
level:99
}
}
}

render() {
return <OriginComponent {...this.props} {...this.state.userInfo}/>
//{...this.props}代表着app中传给子组件的数据. {...this.state.userInfo}代表高阶组件传入的数据
}
}
}

const Home = enhancedUser(function(props) {
return (<h1>Home: {props.name} - {props.level}- {props.banners}</h1>)
//经过加强后,此时父组件和高阶组件传入的数据都可以使用 都在props中
})


export class App extends PureComponent {
render() {
return (
<div>
<Home banners={["xixihaha", "oooaa"]}/>
<div>{this.props.name}</div> //类组件被包裹后,加强组件传入的数据同样也在其props中找
</div>
)
}
}

export default enhancedUser(App) //对一个类组件处理,只用在最后将其包裹

真实场景1: context共享

当存在多个组件需要使用context传递,此时传统方法需要再子组件中写

1
2
3
4
5
6
7
<UserContext.Consumer>
{
value => {
return <h2>Info User: {value.color}</h2>
}
}
</UserContext.Consumer>

从而获取父组件传递的参数,太繁琐.

因此可以对这个包裹部分写成一个高阶组件.使用高阶组件包装子组件,export default withTheme(sonComps) 则可以实现简化.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//父组件
import React, { PureComponent } from 'react'
import ThemeContext from './context/theme_context'
import Product from './Product'

export class App extends PureComponent {
render() {
return (
<div>
<ThemeContext.Provider value={{color:"red", size:30}}> //context传递的
<Product title={"xixihaha"} /> //props传递的
</ThemeContext.Provider>
</div>
)
}
}

export default App


//单独使用一个js文件,创建一个Context对象
import { createContext } from "react";
const ThemeContext = createContext()
export default ThemeContext


//高阶组件包装Context.consumer
import ThemeContext from "./theme_context"
function withTheme(OriginComponent) {
return (props) => {
return(
<ThemeContext.Consumer>
{
value => {
return <OriginComponent {...value} {...props}/>
//注意此处向组件中传递了context的value数据和父组件props的数据,因此子组件取数据时,直接在props统一取就行
}
}
</ThemeContext.Consumer>
)
}
}

export default withTheme


//子组件
import React, { PureComponent } from 'react'
import withTheme from './context/with_theme'

export class Product extends PureComponent {
render() {
const { color, size, title } = this.props //直接存props中取数据
return (
<div>Product:{color}- {size}-{title}</div>
)
}
}

export default withTheme(Product) //调用高阶函数,可以直接

真实场景2:检验授权

传统写法,写一个三元运算{ isLogin?:”需要登录”},为True展示页面,false不展示. 如果存在多个需要登录的地方,那么需要多次重复写这个三元运算.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//检验授权的高阶组件
function loginAuth(OriginComponent) {
return props => {
// ex 从LocalStorage获取token
const token = localStorage.getItem("token") //检验授权的方式
if (token) {
return <OriginComponent {...props}/>
} else {
return <h2> Please Login</h2>
}
}
}

export default loginAuth

//需要授权后才能展示的页面
import React, { PureComponent } from 'react'
import loginAuth from './login_auth'

export class cart extends PureComponent {
render() {
return (
<div>cart</div>
)
}
}

export default loginAuth(cart) //高阶组件包裹

//主App
import React, { PureComponent } from 'react'
export class App extends PureComponent {
render() {
return (
<div>
<cart/> //此时不需要任何额外处理
</div>
)
}
}

export default App

真实场景3:生命周期

测试一个页面的渲染时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function logRenderTime(OriginComponent) {
return class extends PureComponent { //直接返回类时,可以省略类名
UNSAFE_componentWillMount() {
this.beginTime = new Date().getTime()
}

componentDidMount() {
this.endTime = new Date().getTime()
const interval = this.endTime - this.beginTime
console.log(`页面划分${interval}ms渲染`);
}

render() {
return <OriginComponent {...this.props}/>
}
}
}

export default logRenderTime

//某个页面
import React, { PureComponent } from 'react'
export class App extends PureComponent {
render() {
return (
<div>
<h1>xxx</h1>
</div>
)
}
}

export default logRenderTime(App)

Portals的使用

希望渲染的内容独立与父组件,甚至独立于当前挂载到的DOM元素(默认挂载到id为root的DOM元素上)

1
2
3
4
5
6
7
8
9
10
11
12
export class App extends PureComponent {
render() {
return (
<div>
<h1>App H1</h1>
{
createPortal(<h2>appp h2</h2>, document.querySelector("#why")) //(插入的内容, 挂载的地方)
}
</div>
)
}
}

Fragment的用法

可以用于包裹元素块,免得每次总要用div包起来.

和<>等价,不过注意,对于需要绑定key(如列表遍历的情况),只能用Fragment

1
2
3
4
5
6
7
8
9
export default class App extends PureComponent {
render() {
return (
<Fragment>
<h2>xixihaha</h2>
</Fragment>
)
}
}

严格模式

包裹相关组件

1
2
3
4
5
6
7
8
9
10
11
export default class App extends PureComponent {
render() {
return (
<StrictMode>
<div>
<h2>xixihaha</h2>
</div>
</StrictMode>
)
}
}

严格模式检测什么?

  • 识别不安全的生命周期

  • 使用过时的ref API

  • 检查意外的副作用

    • 这个组件的constructor会被调用两次

    • 这是严格模式下故意进行的操作,让你来查看在这里写的一些逻辑代码被调用多次是,是否会产生一些副作用

    • 在生产环境下,是不会被调用两次的

React过渡动画react-transition-group

四个主要组件

  • Transition:和平台无关的组件
  • CSSTransition:通常使用CSSTransition完成过渡动画效果
  • SwitchTransition:两个组件显示和隐藏切换时,使用该组件
  • TransitionGroup:将多个动画组件包裹在其中,一般用于列表中元素的动画

CSSTransition

执行过程中,有三个状态:appear,enter,exit

它有三种状态,需要定义对应的CSS样式

  • 第一类,开始状态,对应的类 -appear, -enter, exit
  • 第二类,执行动画,对应的类 -appear-active, -enter-active, -exit-active
  • 第三类,执行结束,对应的类是-appear-done, -enter-done, -exit-done

CSSTransition常见属性:

  1. className:动画class的名称
  2. timeout:过渡动画的时间
  3. appear是否在初次进行添加动画(需要和in同时设置为true)
  4. unmountOnExit:退出后卸载组件
  5. 部分构造函数,检测动画执行过程,
    1. onEnter:在进入动画之前出发
    2. onEntering:在应用进入动画时被触发
    3. onEntered:在应用进入动画结束后被触发
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
.why-enter {
opacity:0
}

.why-enter-active {
opacity: 1;
transition: opacity 2s ease;
}

.why-exit {
opacity: 1;
}

.why-exit-active {
opacity: 0;
transition: opacity 2s ease;
}

--------------------------------------------
export class App extends PureComponent {
constructor(props) {
super(props)
this.state = {
isShow:true
}
}


render() {
const { isShow} = this.state

return (
<div>
<button onClick={e => this.setState({isShow:!isShow})}>Change</button>
<CSSTransition in={isShow} unmountOnExit={true} classNames="why" timeout={2000} appear
onEnter={e=> console.log("开始进入")}> //属性in表示在true和false中切换
<h2>haha</h2>
</CSSTransition>
</div>
)
}
}

SwitchTransition

主要属性mode:

in-out:新组件先进入,旧组件再移除

out-in:旧组件先移除,新组件再进入

把CSSTransition包起来就可以.主要此时在CSSTransition用的是key而不是 in

1
2
3
4
5
6
7
8
9
10
return (
<div>
<button onClick={e => this.setState({isLogin: !isLogin)}>{ isLogin? "退出":登录}</button>
<SwitchTransition mode='out-in'>
<CSSTransition key={isLogin?"exit":"login"} classNames="login" timeout={2000}>
<h2>haha</h2>
</CSSTransition>
</SwitchTransition>
</div>
)

TransitionGroup

1
2
3
4
5
6
7
      <TransitionGroup component="ul">
<CSSTransition key={index} classNames="book" timeout={2000}>
<li>
<span>{item.name}-{item.price}</span>
</li>
</CSSTransition>
</TransitionGroup>
-------------本文结束感谢您的阅读-------------