React Hook 的使用
React Hook
1. Hook 概述
Hook 是 React 16.8 的新增特性,可以在不写 class 的情况下使用 state 以及其他 React 特性
-
在不写 class 的情况下使用 state 以及其他 React 特性
-
Hook 单词意思是 钩、挂钩、钓钩的意思
-
因此在 React 中,Hook 是指在函数组件中 “ 钩入” React state 以及生命周期等特性的 函数
-
说白了:就是让 函数组件拥有私有数据和钩子函数等
-
Hook 是一个特殊的函数,它可以让你 “ 钩入” React 的特性
-
实际上就是 React 给提供的一些 Hook 函数
-
-
注意事项
- Hook 不能在 class 组件中使用,只能够在函数组件中使用
- 在 React 引入 Hook 以后, React 中的组件分为 函数组件 和 类组件 两种,不能够在称为有无状态组件
通俗的将: Hook 本质就是 JavaScript 函数,可以让开发者不写 class 类的情况下使用 state 以及其他 React 特性
2. 为什么会出现 Hook
为什么使用 Hook 创建组件,不使用类组件了呢 ? 类组件有哪些缺陷呢 ?
之前是如何实现组件状态逻辑复用的 ? render props、HOC
-
组件之间复用状态逻辑复杂
- React 没有提供类似 Vue 的 mixins 以及 小程序的 behavior 特性
- 可以使用 render props 和 HOC 实现组件的复用,但是会让代码很难理解
- 在 React DevTools 中观察过 React 应用,你会发现由 providers,consumers,高阶组件,render props 等其他抽象层组成的组件会形成 “嵌套地狱”
- 可以使用 Hook 从组件中提取状态逻辑,不会对组件的结构进行修改的情况下实现组件复用
-
class 难以理解
- 事件处理的 this 指向问题
- 繁琐的生命周期函数
- 对于函数组件与 class 组件分歧比较大,使用时需要区分两种组件的使用场景
-
总结
- Hook 解决了类组件经常被诟病的一些问题
- Hook 让所有组件都是一个函数,不需要在创建 class 类组件
- 使用类创建组件的方式并不会被取消,被遗弃
- 更加充分体现 React 函数式编程思想,拥抱函数式编程,使代码的变得复用性更高,更加简洁
3. 什么时候使用 Hook
场景:使用函数组件声明 UI 结构,需要使用到 state 私有数据
-
传统做法
- 将函数组件改为类组件,创建 state
-
现在做法
- 直接在项目中使用 Hook 即可
4. Hook 的基本使用
-
首先创建一个 class 类组件,实现简易计算器
import React, { Component } from 'react' export default class App extends Component { constructor() { super() this.state = { count: 1 } } // 实现加 1 的方法 addHandle = () => { this.setState({ count: this.state.count + 1 }) } render() { const { count } = this.state return ( <div> <p>{count}</p> <button onClick={this.addHandle}>加 1</button> </div> ) } } -
使用 Hook 方法实现简易计算器
import React, { useState } from 'react' function App() { // 1. 初始化数据 // 2. 定义操作数据的方法 // 调用 useState 以后,返回一个 state,以及更新 state 的函数 // 第一个参数:返回的状态:在初始化渲染期间,state 就是 useState 传递的第一个值 // 第二个参数:更新 state 的方法,方法名称自定义, const [state, setState] = useState(0) // 点击加 1 的方法 const addHandle = () => { setState(state + 1) } return ( <div> <p>{state}</p> <button onClick={ addHandle }>加 1</button> </div> ) } export default App
5. State Hook 的使用
5.1 声明变量
-
导入 useState 函数
-
调用 useState 函数,并传入初始 state
- 唯一的参数:初始的 state
- 值可以为数字、字符串、对象
- 如果需要存储多个不同的变量,需要调用 useState() 多次
-
定义变量接收返回的值
- 返回值为:当前 state 以及更新 state 的函数
import React, { useState } from 'react' function App() { // 1. 初始化数据 // 2. 定义操作数据的方法 // 调用 useState 以后,返回一个 state,以及更新 state 的函数 // 第一个参数:返回的状态:在初始化渲染期间,state 就是 useState 传递的第一个值 // 第二个参数:更新 state 的方法,方法名称自定义, const [state, setState] = useState(0) // 点击加 1 的方法 const addHandle = () => { setState(state + 1) } return ( <div> <p>{state}</p> <button onClick={ addHandle }>加 1</button> </div> ) } export default App
5.2 使用变量
-
在函数组件中,直接使用 {} 表达式包裹住 state 值即可
<p>{state}</p>
5.3 更新变量
-
在 useState 函数的返回值中包含了setCount 和 count 变量,直接使用即可
const addHandle = () => { setState(state + 1) }
5.4 使用多个 state 变量
-
如果需要存储两个不同的变量,需要调用 useState() 两次
-
注意:不必使用多个 state 变量。State 变量可以很好地存储对象和数组
import React, { useState } from 'react' function App() { // 声明多个变量 const [state, setState] = useState(0) const [name, setName] = useState('亚瑟') const [heros, setHeros] = useState(['亚瑟', '妲己']) const [profile, setProfile] = useState({ name: '黑化后的亚瑟', age: 10 }) const addHandle = () => { // 更新 state setState(state + 1) } const changeName = () => { // 更新 name setName('老亚瑟') } return ( <div> {/* 使用变量 */} <p>{state}</p> <p>{name}</p> <p>{heros[1]}</p> <p>{profile.name}</p> <button onClick={addHandle}>加 1</button> <button onClick={changeName}>更改 name</button> </div> ) } export default App
6. Effect Hook 的使用
Effect Hook 可以让函数组件执行副作用操作
可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合
-
什么是副作用:指的是函数内部与外部互动,产生运算、渲染以外的其他结果
-
副作用主要体现在纯函数中,纯函数是所有函数式编程语言中使用的概念,是一个非常重要的概念
-
什么是纯函数:一个函数的返回结果只依赖 于它的参数,并且在执行过程里面没有与函数外部的数据进行交互
-
纯函数是指相同的输入永远会得到相同的输出,而且没有任何可观察的副作用,而副作用会让函数变的不纯,如果函数依赖外部状态就无法保证输出相同,就会带来副作用。
-
-
常见的副作用操作:数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用
-
在 React 组件中有两种常见副作用操作:需要清除的和不需要清除
6.1 基本使用(无需清除)
案例:在 React 计算器案例中,当 React 对 DOM 进行操作之后,立即更新了 document 的 title 属性
-
class 组件写法
import React, { Component } from 'react' export default class App extends Component { constructor() { super() this.state = { count: 1 } } // 实现加 1 的方法 addHandle = () => { this.setState({ count: this.state.count + 1 }) } componentDidMount () { document.title = `count: ${this.state.count}` } componentDidUpdate () { document.title = `count: ${this.state.count}` } render() { const { count } = this.state return ( <div> <p>{count}</p> <button onClick={this.addHandle}>加 1</button> </div> ) } } -
Hook 写法
-
导入 useEffect 方法
-
useEffect 方法在每次渲染后都会执行,包括第一次渲染以后
-
以后渲染时候方法告诉 React 组件在渲染后需要执行哪些操作
-
-
-
调用 useEffect 方法,传入一个函数作为参数,在函数中进行逻辑的操作
- 在 useEffect 函数参数内部,还能够返回一个函数
- 这个函数是在组件销毁阶段 componentWillUnmount 执行的
- 这个返回的函数只在组件销毁的时候触发
import React, { useState, useEffect } from 'react' function App() { const [state, setState] = useState(0) // 点击加 1 的方法 const addHandle = () => { // 使用 setState 和 state 更新变量 setState(state + 1) } useEffect(() => { document.title = `count: ${state}` }) return ( <div> <p>{state}</p> <button onClick={addHandle}>加 1</button> </div> ) } export default App
6.2 需要清除
案例:在 React 计算器案例中,当 React 对 DOM 进行操作之后,立即更新了 document 的 title 属性
-
class 组件写法
import React, { Component, useState } from 'react' class App extends Component { constructor() { super() this.state = { count: 1 } } // 实现加 1 的方法 addHandle = () => { this.setState({ count: this.state.count + 1 }) } componentDidMount() { document.title = `count: ${this.state.count}` this.timerId = setInterval(() => { console.log('1') }, 1000) } componentDidUpdate() { document.title = `count: ${this.state.count}` } componentWillUnmount () { clearInterval(this.timerId) } render() { const { count } = this.state return ( <div> <p>{count}</p> <button onClick={this.addHandle}>加 1</button> </div> ) } } class Test extends Component { state = { isFlag: true } isFlagHandle = () => { this.setState({ isFlag: false }) } render() { const { isFlag } = this.state return ( <div> { isFlag ? <App></App> : '' } <hr /> <button onClick={this.isFlagHandle}>动态控制 App</button> </div> ) } } export default Test -
Hook 写法
import React, { useState, useEffect } from 'react' function App() { const [state, setState] = useState(0) // 点击加 1 的方法 const addHandle = () => { // 使用 setState 和 state 更新变量 setState(state + 1) } useEffect(() => { const timerId = setInterval(() => { console.log('1') }, 1000) return function () { clearInterval(timerId) } }) return ( <div> <p>{state}</p> <button onClick={addHandle}>加 1</button> </div> ) } function Test () { const [isFlag, setIsFlag] = useState(true) const isFlagHandle = () => { setIsFlag(false) } return ( <div> { isFlag ? <App></App> : '' } <hr /> <button onClick={ isFlagHandle }>动态控制 App</button> </div> ) } export default Test
7. usecontext Hook 的使用
-
调用 React.createContext 方法,产生一个 context 对象
-
useContext 接收一个 context 对象(React.createContext 的返回值)并返回传递的数据,
-
接收到当前的 context 值,由定义的 MyContext.Provider 的 value prop 决定
-
当组件上层最近的 MyContext.Provider 更新时,该 Hook 会触发重渲染
- 并使用最新传递给 MyContext provider 的 context value 值
import React, { useContext } from 'react' const themes = { light: { foreground: "#000000", background: "#eeeeee" }, dark: { foreground: "#ffffff", background: "#222222" } } const ThemeContext = React.createContext(themes.light) function App() { return ( <ThemeContext.Provider value={themes.dark}> <Toolbar /> </ThemeContext.Provider> ) } function Toolbar(props) { return ( <div> <ThemedButton /> </div> ) } function ThemedButton() { const theme = useContext(ThemeContext); return ( <button style={{ background: theme.background, color: theme.foreground }}> I am styled by theme context! </button> ); } export default App
8. useref Hook 的使用
-
useRef 返回一个可变的 ref 对象
-
useRef 的 current 属性被初始化为传入的参数 initialValue
import React, { useRef } from 'react' function App () { const myInput = useRef(null) const handle = () => { // myInput.current.focus() console.log(myInput.current.value) } return ( <div> <p> <input ref={myInput} type="text" /> </p> <button onClick={ handle }>input 高亮</button> </div> ) } export default App
9. 自定义 Hook
自定义 Hook,可以将组件逻辑提取到可重用的函数中
- 当我们想在两个函数之间共享逻辑时,我们通常的做法是将它提取到第三个函数中,
- 组件和 Hook 都是函数,所以也同样适用这种方式
- 自定义 Hook 是一种自然遵循 Hook 设计的约定,而并不是 React 的特性
- 自定义 Hook 是一个函数,函数名约定始终以 use 开头>
9.1 复用坐标点案例
-
自定义 Hook 是一个函数,函数名约定始终以 use 开头>
- 自定义 Hook 必须以 use 开头 !!!
import React, { useState, useEffect } from 'react' // 导入二哈图片 import dog from './haha.jpg' // 封装复用的代码 function useMouse(params) { const [point, setPoint] = useState({ x: 0, y: 0 }) const handle = (e) => { const newPoint = { x: e.clientX, y: e.clientY } setPoint(newPoint) } // 监听鼠标坐标点的值 useEffect(() => { // 监听鼠标移动事件 window.addEventListener('mousemove', handle) // 销毁鼠标移动事件 return function (params) { window.removeEventListener('mousemove', handle) } }) return point } // 坐标点组件 function Point() { // 获取返回的数据 const mouse = Mouse() return ( <div> { mouse.x } -- { mouse.y } </div> ) } // 跟随鼠标移动的哈士奇 function CatMouse(params) { // 获取返回的数据 const mouse = Mouse() return ( <img src={dog} style={{ width: '100px', height: '100px', position: 'absolute', borderRadius: '50%', top: mouse.y - 50, left: mouse.x - 50 }} alt=""/> ) } function App() { return ( <div> <Point /> <CatMouse /> </div> ) } export default App
10. Hook 规则
-
只在最顶层使用 Hook
- 不要在循环、条件判断或嵌套函数中调用 Hook,
- 遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用
-
只在 React 函数组件中调用 Hook
- 不要在普通的 JavaScript 函数中调用 Hook
- 可以在 React 的函数组件中调用 Hook
- 可在自定义 Hook 中调用其他 Hook
.

浙公网安备 33010602011771号