React Hooks
1. React Hooks 概述
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性
产生原因也是解决的问题
- 在组件之间复用状态逻辑很难,为了复用状态和修改状态的逻辑,开发者总结出了 render props 和 高阶组件最佳实践,即使这样用起来还是有些晦涩。
- 复杂组件变得难以理解,表现在同一个生命周期函数中往往包含不相干的逻辑,比如从网络获取数据,设置监听,移除监听等等。
- class 组件内 this 指向(绑定)多种处理方式,导致学习者难以理解。
总结
- 可以不用定义类(class)组件的情况下管理组件状态
- React Hooks 完全向后兼容,原来的类(class)组件不会移除。
- Hooks的知识和开发者以前使用React的知识没有冲突二者可以并行,所以大家不必焦虑之前的知识是否过时。
2. State Hook 的使用
State Hook 的概念
-
State-“状态”,Hook-“钩子”。
-
当组件需要存储和管理状态时,需要启动一个“钩子”,将状态和管理状态的函数“钩入”到当前组件中。
-
如何启动一个引入状态的钩子?
通过调用 useState() 函数
使用 useState() 实现状态管理步骤
-
导入 useState
import React, { useState } from 'react'
-
调用 useState()
参数:状态的初始值
返回值:数组,包含两个元素。第一个是状态,第二个是修改状态的函数
-
解构调用 useState() 返回的数组
const [count, setCount] = useState(0)
-
使用状态值,当前组件内可见
<button onClick={handleCount}>按钮被点击了{count}次</button>
-
使用修改组件状态的函数,当前组件内可见
const handleCount = () => { setCount(count + 1) } return <button onClick={handleCount}>按钮被点击了{count}次</button>
useState() 使用步骤总结
- 导入 { useState }
- 定义函数组件
- 获取状态数据和修改状态数据的函数( useState() )
- 修改状态数据
useState() 初始化其它数据类型
-
初始化类型为字符串
const [msg, changeMsg] = useState("Hello React")
-
初始化类型为数组
const [todos, changeTodos] = useState([ { id: 1, todo: "React" }, { id: 2, todo: "Vue" }, ])
-
不推荐初始化使用对象,因为本身对象就是多个键值对,多个值的维护还是推荐使用多次调用 useState 即可
useState() 注意点
-
在使用修改状态函数的时候乱传值,会怎样?
-
在 UI (JSX 表达式)中,调用修改状态的函数会怎样?
const handleCount = () => { setCount(count + 1) } return <button onClick={handleCount}>按钮被点击了{count}次</button>
const handleCount = () => { setCount(count + 1) } return <button onClick={handleCount}>按钮被点击了{setCount(count + 1)}次</button>
类(class)组件和使用 useState() 创建的函数组件对比
类(class)组件 | useState() |
---|---|
导入 React | 导入 { useState } |
定义类组件 | 定义函数组件 |
定义状态(constructor) | 获取状态数据和修改状态数据的函数(useState()) |
3. Effect Hook 的使用
Effect Hook 的概念
-
Effect-“效果、作用”,Hook-“钩子”。
-
当组件初始化完毕后,需要从网络获取数据或修改DOM时,需要启动一个钩子,去执行对应的操作。
-
如何启动一个获取网络数据或修改DOM的钩子?
通过调用 useEffect() 函数
使用 useEffect() 实现标签 title 显示计数信息
-
导入 useEffect
import React, { useEffect } from 'react'
-
调用 useEffect()
参数:是一个函数,在这个函数中进行业务操作
-
useEffect() 中传入的函数有返回值时要定义为一个函数,作为此次 “Effect” 要执行的清除操作
useEffect(() => { document.title = `按钮被点击了${count}次` return () => { console.log("我们做了清理操作") } })
useEffect() 使用步骤总结
- 导入 { useState, useEffect }
- 定义函数组件
- 获取状态数据和修改状态数据的函数
- 直接在 useEffect() 的参数中定义一个函数,在这个函数中进行DOM操作即可
useEffect() 传入函数的返回值
-
在 useEffect() 参数中定义的函数,如果返回值是一个函数,则 React 会在组件卸载时执行此函数
-
作用是对 useEffect() 调用后,可能引入的全局事件监听或者计时器操作等提供一个清除的时机
useEffect(() => { document.title = `按钮被点击了${count}次` // 引入一个计时器 const timerId = setInterval(() => { console.log("我是一个每隔1秒执行一次的定时器,我的Id是:", timerId) }, 1000) // 返回一个清理函数 return () => { console.log("此次按钮点击我们做了清理操作,清理定时器的Id是:", timerId) // 清除定时器 clearInterval(timerId) } })
useEffect()总结
-
可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个生命周期函数的组合
-
useEffect 的执行时机
useEffect 会在每次渲染后都执行吗?是的,默认情况下,它在第一次渲染之后和每次更新之后都会执行。(我们稍后会谈到如何控制它)你可能会更容易接受 Effect 发生在“渲染之后”这种概念,不用再去考虑"挂载"还是"更新"。react 保证了每次运行 Effect 的同时,DOM 都已经更新完毕
-
useEffect() 传入方法调用返回的清除函数的执行时机
React 何时清除 Effect?React 会在组件卸载的时候执行清除操作。正如之前学到的,Effect 在每次渲染的时候都会执行。这就是为什么 React 会在执行当前 Effect 之前对上一个 Effect 进行清除。我们稍后将讨论为什么这将助于避免 bug 以及如何在遇到性能问题时跳过此行为
4. Context Hook 的使用
Context Hook 的概念
- useContext 让你不使用组件嵌套就可以直接使用Provider中传入的value属性的值
- 通过调用 useContext() 函数
使用useContext()
-
导入 useContext
import React, {useContext} from 'react'
-
使用 React.createContext() 创建的 Context
-
调用 useContext()
参数:第二步创建的 Context
<div> <p>{useContext(ThemeContext)}</p> </div>
5. Hook 规则
第一条
不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确
// ------------
// 首次渲染
// ------------
useState('Mary') // 1. 使用 'Mary' 初始化变量名为 name 的 state
useEffect(persistForm) // 2. 添加 effect 以保存 form 操作
useState('Poppins') // 3. 使用 'Poppins' 初始化变量名为 nickname 的 state
useEffect(updateTitle) // 4. 添加 effect 以更新标题
// -------------
// 二次渲染
// -------------
useState('Mary') // 1. 读取变量名为 name 的 state(参数被忽略)
useEffect(persistForm) // 2. 替换保存 form 的 effect
useState('Poppins') // 3. 读取变量名为 nickname 的 state(参数被忽略)
useEffect(updateTitle) // 4. 替换更新标题的 effect
// ...
//