React Hooks

React Hooks

React Hooks 简介

React Hooks就是用函数形式代替原来的继承类的形式,并且使用预函数的形式管理state,有Hooks可以不再使用类的形式定义组件了。原来把组件分为有状态组件和无状态组件,有状态组件用类的形式声明,无状态组件用函数的形式声明。现在所有的组件都可以用函数来声明了。

Hooks本质上就是一类特殊的函数,他们可以为你的函数型组件(function component)注入一些特殊的功能。

原始写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React, { Component } from 'react';

class Example extends Component {
constructor(props) {
super(props);
this.state = { count:0 }
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={this.addCount.bind(this)}>Chlick me</button>
</div>
);
}
addCount(){
this.setState({count:this.state.count+1})
}
}

export default Example;

React Hooks 写法:

1
2
3
4
5
6
7
8
9
10
11
import React, { useState } from 'react';
function Example(){
const [ count , setCount ] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={()=>{setCount(count+1)}}>click me</button>
</div>
)
}
export default Example;

useState

1
const [ count , setCount ] = useState(0);

count为定义的变量,为它赋予初始值0,setCount为其对应的方法;

1
<p>You clicked {count} times</p>

读取它的值,只要使用{count}就可以了

1
<button onClick={()=>{setCount(count+1)}}>click me</button>

直接调用setCount函数,这个函数接收的参数是修改过的新状态值 。

接下来的事情就交给React,他会重新渲染组件。React自动帮助我们记忆了组件的上一次状态值 。

React Hooks不能出现在条件判断语句中,因为它必须有完全一样的渲染顺序

useEffect

代替componentDidMountcomponentDidUpdate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React, { useState , useEffect } from 'react';
function Example(){
const [ count , setCount ] = useState(0);

useEffect(()=>{
console.log(`useEffect=>You clicked ${count} times`)
})

return (
<div>
<p>You clicked {count} times</p>
<button onClick={()=>{setCount(count+1)}}>click me</button>
</div>
)
}
export default Example;

注意:

  1. React首次渲染和之后的 每次渲染都会调用一遍useEffect函数,而之前我们要用两个生命周期函数分别表示首次渲染(componentDidMonut)和更新导致的重新渲染(componentDidUpdate)。
  2. useEffect中定义的函数的执行不会阻碍浏览器更新视图,也就是说这些函数时异步执行的,而componentDidMonutcomponentDidUpdate中的代码都是同步执行的。个人认为这个有好处也有坏处吧,比如我们要根据页面的大小,然后绘制当前弹出窗口的大小,如果时异步的就不好操作了。

实现componentWillUnmount生命周期函数

在写React应用的时候,在组件中经常用到componentWillUnmount生命周期函数(组件将要被卸载时执行)。比如我们的定时器要清空,避免发生内存泄漏;比如登录状态要取消掉,避免下次进入信息出错。

1
2
3
4
5
6
7
8
9
function Index() {
useEffect(()=>{
console.log('useEffect=>老弟你来了!Index页面')
return ()=>{
console.log('老弟,你走了!Index页面')
}
},[])
return <h2>JSPang.com</h2>;
}

return用与解绑组件。

useEffect的第二个参数,它是一个数组,数组中可以写入很多状态对应的变量,意思是当状态值发生变化时,我们才进行解绑。但是当传空数组[]时,就是当组件将被销毁时才进行解绑,这也就实现了componentWillUnmount的生命周期函数。

useContext

useContext,它可以帮助我们跨越组件层级直接传递变量,实现共享。

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
import React, { useState , createContext } from 'react';
//===关键代码
const CountContext = createContext()

function Example4(){
const [ count , setCount ] = useState(0);

return (
<div>
<p>You clicked {count} times</p>
<button onClick={()=>{setCount(count+1)}}>click me</button>

<CountContext.Provider value={count}>
<Counter />
</CountContext.Provider>

</div>
)
}

//子组件
function Counter(){
const count = useContext(CountContext) //一句话就可以得到count
return (<h2>{count}</h2>)
}

export default Example4;

使用const CountContext = createContext()创建全局上下文;再使用CountContext.Provider实现共享,在子组件中使用useContext()接收值。

useReducer

reducer其实就是一个函数,这个函数接收两个参数,一个是状态,一个用来控制业务逻辑的判断参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React, { useReducer } from 'react';

function ReducerDemo(){
const [ count , dispatch ] =useReducer((state,action)=>{
switch(action){
case 'add':
return state+1
case 'sub':
return state-1
default:
return state
}
},0)
return (
<div>
<h2>现在的分数是{count}</h2>
<button onClick={()=>dispatch('add')}>Increment</button>
<button onClick={()=>dispatch('sub')}>Decrement</button>
</div>
)
}
export default ReducerDemo

useMemo

useMemo主要用来解决使用React hooks产生的无用渲染的性能问题。使用function的形式来声明组件,失去了shouldCompnentUpdate(在组件更新之前)这个生命周期,也就是说我们没有办法通过组件更新前条件来决定组件是否更新。而且在函数组件中,也不再区分mountupdate两个状态,这意味着函数组件的每一次调用都会执行内部的所有逻辑,就带来了非常大的性能损耗。useMemouseCallback都是解决上述性能问题的 。

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
import React , {useState,useMemo} from 'react';

//父组件
function Example3(){
const [count1 , setCount1] = useState(0)
const [count2 , setCount2] = useState(100)
return (
<>
<button onClick={()=>{setCount1(count1+1)}}>count1加1</button>
<button onClick={()=>{setCount2(count2-1)}}>count2减1</button>
<ChildComponent name={count2}>{count1}</ChildComponent>
</>
)
}

//子组件
function ChildComponent({name,children}){
function changeCount2(name){
console.log('count2减1了!!')
return name
}

const actionCount2 = useMemo(() => changeCount2(name), [name])
// const actionCount2 = changeCount2(name)
return (
<>
<div>count1:{children}</div>
<div>count2:{actionCount2}</div>
</>
)
}

自定义Hooks函数

其实自定义Hooks函数和用Hooks创建组件很相似,跟我们平时用JavaScript写函数几乎一模一样,可能就是多了些React Hooks的特性,自定义Hooks函数偏向于功能,而组件偏向于界面和业务逻辑。由于差别不大,所以使用起来也是很随意的。如果是小型项目是可以的,但是如果项目足够复杂,这会让项目结构不够清晰。所以学习自定义Hooks函数还是很有必要的。

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
function useWinSize(){
const [ size , setSize] = useState({
width:document.documentElement.clientWidth,
height:document.documentElement.clientHeight
})

const onResize = useCallback(()=>{
setSize({
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
})
},[])
useEffect(()=>{
window.addEventListener('resize',onResize)
return ()=>{
window.removeEventListener('resize',onResize)
}
},[])

return size;
}

function Example9(){

const size = useWinSize()
return (
<div>页面Size:{size.width}x{size.height}</div>
)
}

export default Example9