一. 简述
在以往的react16.8前, 我们React组件创建方式基本包含两种方式: 类组件, 纯函数组件。 React 团队希望组件最佳写法应该是函数,而不是类。
然而常规的纯函数组件有以下特点:
- 没有状态
- 没有生命周期
- 没有 this
因存在如上特点,使得纯函数组件只能做UI展示的功能, 涉及到状态的管理与切换就不得不用到类组件或这redux。 但因为简单的页面也是用类组件,同时要继承一个React实例,使得代码会显得很重。
‘Hooks’ 单词意思为:钩子。
React Hooks 意思是:组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码“钩”进来。
React Hooks常用钩子有如下四种:
- useState()
- useContext()
- useReducer()
- useEffect()
使用hooks 我们会发现没有了继承,渲染逻辑,生命周期等, 代码看起来更加的轻便简洁了。
React 约定,钩子一律使用 use 前缀命名 (自定义钩子都命名为:useXXXX)
二. 常用举例及原理分析
useState 状态钩子
官方示例:
import React, {useState} from 'react'
const AddCount = () => {
const [ count, setCount ] = useState(0)
const addcount = () => {
let newCount = count
setCount(newCount+=1)
}
return (
<>
<p>{count}</p>
<button onClick={addcount}>count++</button>
</>
)
}
原理:
// 我们实现一个简易版的useState
let memoizedStates = [ ] // 多个useState 时需要使用数组来存
let index = 0
function useState (initialState) {
memoizedStates[index] = memoizedStates[index] || initialState
let currentIndex = index;
function setState (newState) {
memoizedStates[currentIndex] = newState
render()
}
return [memoizedStates[index++], setState]
}
useContext 共享状态钩子
该钩子的作用是,在组件之间共享状态。 可以解决react逐层通过Porps传递数据。
import React,{ useContext } from 'react'
const ShareContext= () => {
const AppContext = React.createContext({})
const A =() => {
const { name } = useContext(AppContext)
return (
<p>我是A组件的名字{name}<span>我是A的子组件{name}</span></p>
)
}
const B =() => {
const { name } = useContext(AppContext)
return (
<p>我是B组件的名字{name}</p>
)
}
return (
<AppContext.Provider value={{name: 'hook测试'}}>
<A/>
<B/>
</AppContext.Provider>
)
}
export default ShareContext
useContext(context) 是针对 context (上下文) 提出的api。 它接受React.createContext()的返回结果作为参数(也就是context对象并返回最近的context)。 使用useContext将不再需要Provider 和 Consumer。当最近的context更新时,那么使用该context的hook将会重新渲染。
useReducer(): Action 钩子
React本身不提供状态管理。 而useReducer() 提供了状态管理。其基本原理是通过用户在页面中发起action, 从而通过reducer方法来改变state, 从而实现页面和状态的通信。
import React,{useReducer} from 'react'
const MyReducer= () => {
const reducer = (state, action) => {
if(action.type === ''add){
return {
...state,
count: state.count +1,
}
}else {
return state
}
}
const addcount = () => {
dispatch({
type: 'add'
})
}
const [state, dispatch] = useReducer(reducer, {count: 0})
return (
<>
<p>{state.count}</p>
<button onClick={addcount}>count++</button>
</>
)
}
export default MyReducer
使用useReducer() 代替了Redux的功能, 但useReducer 无法提供中间件等功能。
useEffect(): 副作用钩子
React常规开发中,我们习惯将一些处理副作用的操作,如:异步请求等 放在特定的生命周期中。 useEffect 则是为函数组件提供了副作用的钩子。
useEffect() : 接收两个参数, 第一个是进行的异步操作, 第二个是数组,用来给出Effect的依赖项。第二个参数(数组)发生变化, ==useEffect()==就会执行。第二项不填时, useEffect() 会在每次组件渲染时执行。
import React, { useState, useEffect } from 'react'
const AsyncPage = ({name}) => {
const [loading, setLoading] = useState(true)
const [person, setPerson] = useState({})
// useEffect 类似compomnetDidMount 初始化会执行,更新时会执行
useEffect(() => {
setLoading(true)
setTimeout(()=> {
setLoading(false)
setPerson({name})
},3000)
},[name])
return (
<div>
{loading?<p>Loading...</p>:<p>{person.name}</p>}
<div/>
)
}
const PersonPage = () =>{
const [state, setState] = useState('')
const changeName = (name) => {
setState(name)
}
return (
<>
<AsyncPage name={state}/>
<button onClick={() => {changeName('名字1')}}>名字1</button>
<button onClick={() => {changeName('名字2')}}>名字2</button>
</>
)
}
export default PersonPage
创建自己的Hooks
有时候我们需要创建自己想要的Hooks, 来满足更便捷的开发。
import React, { useState, useEffect } from 'react'
const usePerson = (name) => {
const [loading, setLoading] = useState(true)
const [person, setPerson] = useState({})
useEffect(() => {
setLoading(true)
setTimeout(()=> {
setLoading(false)
setPerson({name})
},2000)
},[name])
return [loading,person]
}
const AsyncPage = ({name}) => {
const [loading, person] = usePerson(name)
return (
<div>
{loading?<p>Loading...</p>:<p>{person.name}</p>}
</div>
)
}
const PersonPage = () =>{
const [state, setState]=useState('')
const changeName = (name) => {
setState(name)
}
return (
<div>
<AsyncPage name={state} />
<button onClick={() => {changeName('名字1')}}>名字1</button>
<button onClick={() => {changeName('名字2')}}>名字2</button>
</div>
)
}
export default PersonPage
上面自定义了一个Hooks, 它接受一个字符串, 返回一个数组, 数组中的两个状态在使用usePerson() 时, 会根据传入的不同反而返回不同的状态。
React Hooks 概要及未来
根据官方文档的说法:
- 完全可选
- 100%向后兼容 (Hook 不会含任何破坏性改动)
- 现在可用 (Hook 已发布于 v16.8.0)
- 没有计划从React中移除class
- Hook不会影响对React概念的理解 (Hook 为React提供了相关API:props, state,context, refs以及生命周期)