Hooks是什么?
Hooks是一些能够在函数组件中"钩入"React state 及生命周期等特性的函数。是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
作用: 为函数组件提供状态、生命周期等原本 在Class 组件中才提供的功能
注意:
- Hooks只能在函数组件中使用
- 可以理解为通过 Hooks 为函数组件钩入 class 组件的特性
Hooks 前后,组件开发模式的对比
- React v16.8 以前: class 组件(提供状态) + 函数组件(展示内容)
- React v16.8 及其以后:
(1)class 组件(提供状态) + 函数组件(展示内容)
(2)Hooks(提供状态) + 函数组件(展示内容)
(3)混用以上两种方式:部分功能用 class 组件,部分功能用 Hooks+函数组件
为什么要有 Hooks
react组件的本质
React 是用于构建用户界面的 JavaScript 库 。
React组件是对特定功能的封装,主要用来对UI进行拆分。
React 组件的模型其实很直观,就是从 Model 到 View 的映射,这里的 Model 对应到 React 中就是 state 和 props
公式:组件(State+Props) = UI
class 组件自身的问题
在根据状态来渲染UI这件事上,class 组件并没有发挥它最重要的功能:
- 组件之间很少继承
- 组件之间很少相互访问
函数式组件的好处
- 函数本身比较简单,更好的胜任根据状态来渲染UI这件事
- hooks让函数组件内部有了维护状态的能力
- hooks带来了组件的逻辑复用能力
hooks的使用策略
(1)react没有计划从React中移除class
(2)Hook 和现有代码可以同时工作,你可以渐进式地使用:
- 不推荐直接使用 Hooks 大规模重构现有组件
- 推荐新功能用 Hooks,复杂功能实现不了的,也可以继续用 class
- 找一个功能简单、非核心功能的组件开始使用 hooks
(3)class 组件相关的 API 在hooks中可以不用
- state与setState
- 钩子函数,
componentDidMount
、componentDidUpdate
、componentWillUnmount
this
相关的用法
(4)原来学习的内容还需要使用
- JSX:
{}
、onClick={handleClick}
、条件渲染、列表渲染、样式处理等 - 组件:函数组件、组件通讯
- React 开发理念:
单向数据流
、状态提升
等
useState-基本使用
使用场景
当你想要在函数组件中,使用组件状态时,就要使用 useState 这个Hook 了,
使用步骤
(1) 导入useState函数
(2) 调用 useState
函数,传入初始值,返回状态
和修改状态的函数
(3) 使用
- 在 JSX 中展示
状态
- 特定的时机调用修改状态的函数来改状态
示例:
import { useState } from 'react'
// useState 是hook,hook是use开头的函数
const Count = () => {
// 下面三句可以使用 const [state,setState] = useState(0)直接替代
// 0 是初始值
// 返回值是一个数组
const stateArray = useState(0)
// 状态值 -> 0
const state = stateArray[0]
// 修改状态的函数
const setState = stateArray[1]
return (
<div>
{/* 展示状态值 */}
<h1>useState Hook -> {state}</h1>
{/* 点击按钮,让状态值 +1 */}
<button onClick={() => setState(state + 1)}>+1</button>
</div>
)
}
-
参数:状态初始值。比如,传入 0 表示该状态的初始值为 0
(1)注意:此处的状态可以是任意值(比如,数值、字符串等),而 class 组件中的 state 必须是对象
-
返回值:数组,包含两个值:1 状态值(state) 2 修改该状态的函数(setState)
useState-处理表单元素
- 用useState初始化内容和修改内容的方法
- 向input元素上设置value和onChange属性
import React, { useState } from 'react'
import ReactDom from 'react-dom'
export default function App () {
const [content, setContent] = useState('')
return (
<div>
{content}
<input value={content} onChange={(e) => setContent(e.target.value)} />
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))
useState-setXXX回调函数格式
useState 两种格式
格式1:传入值
useState(0) useState('abc')
格式2:传入回调
useState(() => { return 初始值 })
(1)回调函数的返回值就是状态的当前值
(2)回调函数只会触发一次
使用场景
格式1:传入值
如果状态就是一个普通的数据(比如,字符串、数字、数组等)都可以直接使用 useState(普通的数据)
格式2:传入回调
初始状态需要经过一些计算得到。 useState(()=>{这里有一些计算, return 结果}))
setXXX的参数可以是回调
状态需要迭代累计。 setXXXXX((上一次的值) => { return 新值 })
示例:
import React, { useState } from 'react'
import ReactDom from 'react-dom'
export default function App () {
const [count, setCount] = useState(0)
const hClick1 = () => {
setCount(count+1)
setCount(count+1)
setCount(count+1)
}
const hClick2 = () => {
setCount((count) => {
return count + 1
})
setCount((count) => {
return count + 1
})
setCount((count) => {
return count + 1
})
}
return (
<div>
count:{count}
<button onClick={hClick1}>多次连续setCount-值</button> // 1
<button onClick={hClick2}>多次连续</button> // 3
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))
useState-组件的更新过程
示例:
import { useState } from 'react'
const Count = () => {
console.log('Count...')
const [count, setCount] = useState(0)
return (
<div>
{/* 展示状态值 */}
<h1>useState Hook -> {count}</h1>
{/* 点击按钮,让状态值 +1 */}
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
)
}
更新过程
函数组件使用 useState hook 后的执行过程,以及状态值的变化:
-
组件第一次渲染:
(1)执行该函数组件中的代码
(2)调用
useState(0)
将传入的参数作为状态初始值,即:0(3)渲染组件,此时,获取到的状态 count 值为: 0
用户点击按钮,调用 setCount(count + 1)
修改状态,因为状态发生改变,所以,该组件会重新渲染
-
组件第二次渲染:
(1)再次执行该组件中的代码逻辑
(2调用
useState(0)
:(3)此时React 内部会拿到最新的状态值而非初始值
(4)该案例中最新的状态值为 1
(5)再次渲染组件,此时,获取到的状态 count 值为:1
useState 的初始值(参数)只会在组件第一次渲染时生效
const [count, setCount] = useState(()=>{
console.log('useState...') // 这句只会输出一次
return 0
})
也就是说,以后的每次渲染,useState 获取到都是最新的状态值。React 组件会记住每次最新的状态值!
总结如下:
- 状态更新,整个组件的逻辑重新运行一次
- useState只会在组件第一次渲染时使用状态的初值,随后都会使用状态的最新值
- useState 这个 Hook 就是用来管理 state 的,它可以让函数组件具有维持状态的能力。也就是说,在一个函数组件的多次渲染之间,这个 state 是共享的。
如何为函数组件提供多个状态?
- 方案1:
useState({状态1, 状态2.....})
- 方案2:
useState(状态1) useState(状态2)
推荐使用方案2
调用 useState
Hook 多次即可,每调用一次 useState Hook 可以提供一个状态。
注意:
hooks: setXXX(新值) ==> 用新值去替换之前的值
class: setState({要修改的属性})
useState的使用规则
规则
-
seState只能直接出现在 函数组件 内部
-
useState不能嵌套在 if/for
原因: React 是按照 Hooks 的调用顺序来识别每一个 Hook,如果每次调用的顺序不同,导致 React 无法知道是哪一个 Hook。 可以通过开发者工具进行查看
seEffect 副作用
函数式组件:
- 主作用:就是根据数据(state/props)渲染 UI
- 副作用:数据(Ajax)请求、手动修改 DOM、开启定时器,清空定时器,添加事件监听,删除事件, localStorage 操作等
useEffect-基本使用
使用步骤
// 1. 导入useEffect
import { useEffect } from 'react'
// 2. 使用useEffect
useEffect(() => {
console.log('useEffect 1 执行了,可以做副作用')
})
useEffect(() => {
console.log('useEffect 2 执行了,可以做副作用')
})
执行时机
render工作完成之后,执行Effect;
如果定义了多个,则顺序执行;
示例:
import React, { useState, useEffect } from 'react'
import ReactDom from 'react-dom'
export default function App () {
useEffect(() => {
console.log('useEffect')
document.title = 'count' + count
})
const [count, setCount] = useState(0)
return (
<div
onClick={() => {
setCount(count + 1)
}}>
函数组件{count}
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))
useEffect的依赖项
useEffect有两个参数:
参数1: 副作用函数。
参数2:执行副作用函数的依赖项:它决定了什么时机执行参数1(副作用函数)
useEffect的完整格式
情况1:不带第二个参数。执行时机:每次更新之后都要执行
情况2:带第二个参数,参数是空数组。执行时机:只执行第一次
useEffect(() => {
// 副作用函数的内容
}, [])
使用场景:1 事件绑定 2 发送请求获取数据 等。
情况3:带第二个参数(数组格式),并指定了依赖项。执行时机:(1)初始执行一次 (2)依赖项的值变化了,执行一次
useEffect(() => {
// 副作用函数的内容
}, [依赖项1,依赖项2,....])
这里的依赖项就是组件中定义的状态。
参考:
(1) useEffect完全指南:overreacted.io/zh-hans/a-c…