内容概要
- React Hooks 介绍
- React Hooks 使用
- 自定义 Hook
一、React Hooks 介绍
React Hooks 是用来做什么的
- 对函数组件进行增强,让函数型组件可以存储状态,可以拥有处理副作用的能力
- 让开发者在不使用类组件的情况下,实现相同的功能
类组件的不足(Hooks 要解决的问题)
1、缺少逻辑复用机制
- 为了复用逻辑增加无实际渲染效果的组件,增加了组件层级 显示十分臃肿
- 增加了调试的难度以及运行效率的降低
2、类组件经常会变的很复杂难以维护
- 将一组相干的业务逻辑拆分到多个生命周期函数中
- 在一个生命周期函数内存在多个不相干的业务逻辑
3、类成员方法不能保证this指向的正确性
二、React Hooks 使用
Hooks 意为钩子, React Hooks 就是一堆钩子函数, React 通过这些钩子函数对函数型组件进行增强,不同的钩子函数提供了不同的功能
- useState()
- useEffects()
- useReduce()
- useRef()
- useCallback()
- useContext()
-
1、useState()
用于为函数组件引入状态
- 函数型组件原来是不能保存状态数据的
- useState内部是使用闭包来实现保存状态数据的
- 使用
- 接收唯一的参数即状态初始值,初始值可以是任意数据类型
- 返回值为数组, 数组中存储状态值和更改状态值的方法, 方法名称约定以set开头,后面加上状态名字
- 方法可以被调用多次,用以保存不同的状态
- 参数可以是一个函数,函数返回什么,初始状态就是什么,函数只会被调用一次,用在初始值是动态值的情况
- 设置状态值方法的参数可以是一个值也可以是一个函数
- 设置状态值方法的方法本身是异步的 ```typescript const [count, setCount] = useState(0)
setCount((count) => { const newCount = count + 1 console.log(newCount) // 1 return newCount })
- **补充 问:this.setState是同步还是异步**
- **答: **
- **在组件的生命周期或者React合成事件中,是异步**
- **在setTimeout或者原生dom事件中,是同步**
<a name="UbjtL"></a>
#### 2、useReducer
- **useReducer是另一种让函数组件保存状态的方式**
```typescript
function App() {
function reducer(state, action) {
switch(action.type)
case "increment":
return state - 1;
case "decremen":
return state + 1;
default:
return state;
}
const [count, dispatch] = useReducer(reducer, 0)
return <div>
<button onClick={() => {dispatch({ type: 'decremen' })}}>-1</button>
<span>{count}</span>
<button onClick={() => {dispatch({ type: 'increment' })}}>-1</button>
</div>
}
3、useContext
- 在跨组件层级获取数据时简化获取数据的代码 ```typescript import React, { createContext, useContext } from react; const contextCount = createContext();
function App() {
return
function Foo() { const value = useContext(contextCount); return
export default App;
<a name="hOWVk"></a>
#### 4、useEffect()
- **让函数型组件拥有处理副作用的能力,类似声明周期函数**
- **执行时机,可以把useEffect 看作 componentDidMount, componentDidUpdate 和 componentWillUnmount这三个声明周期的组合**
- **useEffect(() => {} ) ===> componentDidMount, componentDidUpdate**
- **useEffect(() => {}, [] ) ====> componentDidMount**
- **useEffect(() => () => {} ) ====> componentWillUnMount**
```typescript
import React, { useState, useEffect } from 'react'
function App() {
const [ count, setCount ] = useState(0);
// 1.count 改变的时候 都会触发
// useEffect(() => {
// console.log(123) //
// })
// 2.页面初次加载的时候 触发一次 (组件挂载完成后执行)
// useEffect(() => {
// console.log(123)
// },[])
// 3. 组件 被卸载的时候 触发
// useEffect(() => {
// return () => {
// console.log('组件被卸载了')
// }
// })
return <div>{count}</div>
}
- useEffect 可以在一个文件中被多次调用,优点
- 按照用途将代码进行分类(将一组相干的业务逻辑归置到了同一个副作用函数中)
- 简化重复代码,使组件内部代码更加清晰
- useEffect 只有指定数据发生变化时触发effect,指定数据放在第二个参数的数组中
- useEffect 结合异步函数
- useEffect 中的参数函数不能是异步函数, 因为useEffect 函数要返回清理资源的函数, 如果是异步函数就变成了返回Promise ```typescript
useEffect(() => { (async function() => { await axios.get(); })() })
<a name="LlJ5z"></a>
#### 5、useMemo() 返回计算后的新值
- **useMemo 的行为类似 Vue 中的计算属性, 可以监测某个值的变化, 根据变化值计算新值**
- **性能优化,会缓存计算结果, 如果监测值没有发生变化, 即使组件重新渲染, 也不会重新计算。 此行为可以有助于避免在每个渲染上进行昂贵的计算**
```typescript
import { usemMemo } from 'react';
const result = useMemo( () => {
// 如果count 发生变化 此函数重新执行
.....
return result;
}, [count])
6、memo
- 性能优化, 如果本组件中的数据没有发生变化,阻止组件更新
- 类似组件中的PureComponent 和 shouldComponentUpdate ```typescript import React, { memo } from ‘react’;
function App() {
return
export default memo(App);
<a name="iEUc9"></a>
#### 7、useCallback() 接收函数返回一个新的函数
- **性能优化,缓存函数, 使组件重新渲染时得到相同的函数实例**
```typescript
import React, { useCallback, useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
const newUseCount = useCallback((count) => { setCount( count + 1)}, [count])
return <div>
<span>{count}</span>
<button onClick={ (count) => {useCount( count + 2)}}> +1 </button>
// 通过useCallback 包装后, 上面事件调用导致count改变时 不会频繁的渲染下面的Test 组件
// 来达到性能优化的效果
<Test newUseCount={newUseCount} />
</div>
}
8、useRef
- 获取DOM 元素对象 ```typescript import React, { useRef } from ‘react’;
function App() { const username = useRef(); const handler = () => { console.log(usename) } return }
- **保存数据(跨组件周期)例如清除定时器**
- **即使组件重新渲染, 保存的数据仍然还在, 保存的数据被更改不会触发组件重新渲染**
```typescript
import React, { useState, useRef, useEffect } from 'react'
function App() {
const [count, setCount] = useState(0);
const timer = useRef
useEffect(() => {
timer.current = setInterVal(() => {
setCount((cout) => count + 1)
}, 1000)
}, [])
stopInterVal = () => {
clearInterval(timer.current)
}
return <div>
<button onClick={() => { stopInterVal }}>停止计时器</button>
<span>{count}</span>
</div>
}
9、自定义Hook
目的就是实现组件之间的逻辑共享
- 自定义 Hook 是标准的封装和共享逻辑的方式
- 自定义 Hook 是一个函数, 其名称以 use 开头
- 自定义 Hook 其实就是逻辑和内置 Hook 的组合 ```typescript import React, { useState, useEffect } from ‘react’ import axios from ‘axios’
// 自定义 Hook
function useGetData() { const [post, setPost] = useState({}) useEffect({ axios.get(“接口名”) .then(response => setPost(response)) }, []) return [post, setPost] }
function App() { const [post, setPost] = useState(); return
export default App;
- **自定义 Hook 在表单中的应用**
```typescript
import React, { useState } from 'react'
function useUpdateInput(initValue) {
const [value, setValue] = useState(initValue)
return {
value,
onChange: (event) => { setValue(event.target.value)}
}
}
function App() {
const useNameInput = useUpdateInput("");
const usePassInput = useUpdateInput("");
const submitForm = (event) => {
event.preventDefault()
console.log(useNameInput.value)
console.log(usePassInput.value)
}
return <form onSubmit={submitForm}>
<input type="text" name="useName" {...useNameInput} />
<input type="text" name="password" {...usePassInput} />
<input type="submit" />
</form>
}
三、React 路由 Hooks
1、react-router-dom 路由提供的钩子函数
- useHistory
- useLocation
- useRouteMatch
- useParams
import { useHistory, useLocation, useRouteMatch, useParams } from ‘react-router-dom’ 使用的时候 通过 useHistory() 调用获取
四、实现useState(原理)
import React form 'react'
import ReactDOM from 'react-dom'
let state = [];
let setters = [];
let stateIndex = 0;
function createSetter(index) {
// 下面这个方法就是 useState的第二个参数
return function(newValue) {
state[index] = newValue;
render()
}
}
function useState(initialState) {
state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState;
setters.push(createSetter(stateIndex))
let value = state[stateIndex]
let setter = setters[stateIndex]
stateIndex++;
return [value, setter]
}
function render() {
stateIndex = 0
ReactDOM.render(<APP />, document.getElementById('root'))
}
五、实现 useEffect 钩子函数
let prevDepsAry = []
let effectIndex = 0
function useEffect(callback, depsAry) {
// 判断callback 是不是函数
if(Object.prototype.toString.call(callback) !== '[object Function]')
throw new Error('不是函数')
// 判断第二个参数是否为空
if( typeof depsAry === 'undefined') {
callback();
} else {
// 判断despsAry 是不是数组
if( Object.prototypr.toString.call(depsAry) !== '[object Array]')
throw new Error('不是数组')
// 获取上一次的状态值
let prevDeps = prevDepsAry[effectIndex];
// 将当前的依赖值和上一次的依赖值作对比 如果有变化 调用callback
let hasChanged = prevDeps ? depsAry.every((dep, index) => dep === prevDeps) : true
if ( hasChanged) {
callback();
}
// 同步依赖值
prevDepsAry[effectIndex] = depsAry
effectIndex++;
}
}
六、实现useReducer 钩子函数
function useReducer(reducer, inititalState) {
const [state, setState] = useState(inititalState)
function dispatch(action) {
const newState = reducer(state, action)
setState(newState);
}
return [state, dispatch]
}