Hooks: 钩子 , 钓钩 , 钩住 ,是一些可以让你在函数组件里"钩入" React state 以及生命周期等特性的函数 ,是 React 16.8 的新增特性, 它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性
作用: 为函数组件提供状态, 生命周期等原本在 Class组件中才提供的功能
总结:
componentDidMount
、componentDidUpdate
、componentWillUnmount
{}
、onClick={handleClick}
、条件渲染、列表渲染、样式处理等单向数据流
、状态提升
等使用步骤
useState
函数useState
函数 , 传入初始值 , 返回 状态和 修改状态的函数import React, { useState } from 'react'
import ReactDOM from 'react-dom'
export default function App () {
const [count, setCount] = useState(0)
console.log(count)
return (
<div>
{count}
<button onClick={() => { setCount(count + 1) }}>点击+1</button>
</div>
)
}
ReactDOM.render(<App/>, document.getElementById('root'))
使用数组解构简化 useState
的使用。约定:修改状态的函数名称以 set 开头,后面跟上状态的名称
状态
和生命周期
状态值
和修改状态的方法
步骤:
useState
初始化内容和修改内容的方法value
和 onChange
属性import React, { useState } from 'react'
import ReactDOM from 'react-dom'
export default function App () {
const [inputVal, setinputVal] = useState('')
console.log(inputVal)
return (
<div>
<input value={inputVal} onChange={(e) => { setinputVal(e.target.value) }} />
</div>
)
}
ReactDOM.render(<App/>, document.getElementById('root'))
useState
可以轻松的完成受控组件的功能
格式一: 传入值
useState(0) or useState('abc')
格式二: 传入回调
useState((=>{ return 初始值}))
格式一: 传入值
如果状态就是一个普通的数据(比如, 字符串 , 数字 , 数组等) 都可以直接使用 useState(普通数据)
格式二: 传入回调
useState(()=>{这里可以进行计算 , return 结果})
状态需要迭代累计的时候, setXXX((上一次的值)=>{return 新值})
示例
import React, { useState } from 'react'
import ReactDOM from 'react-dom'
export default function App () {
const [count, setCount] = useState(1)
const hClick = () => {
setCount(count + 1)
setCount(count + 1)
setCount(count + 1)
}
return (
<div>
count:{count}
<button onClick={hClick}>多次连续setCount</button>
</div>
)
}
ReactDOM.render(<App/>, document.getElementById('root'))
我们可以看到,不是回调函数的情况下,它和 类组件
中的 setState
,都会有同样的情况,那就是 多次触发会 合并 ,我们认为的值 应该是 4, 可是输出的却是 2
如果想实现,count能够迭加的效果, 那我们就需要使用 回调函数为参数 , 在回调的形参中能够接收到上一次的值
代码改造
import React, { useState } from 'react'
import ReactDOM from 'react-dom'
export default function App () {
const [count, setCount] = useState(1)
const hClick = () => {
setCount((count) => count + 1)
console.log(count)
setCount((count) => count + 1)
console.log(count)
setCount((count) => count + 1)
console.log(count)
}
return (
<div>
count:{count}
<button onClick={hClick}>多次连续setCount</button>
</div>
)
}
ReactDOM.render(<App/>, document.getElementById('root'))
这次就能成功的实现迭加的效果,但是为什么会 console.log
会输出的都是 1 呢 ? 这点我在类组件中讲到过, 还是因为 React的性能优化机制,导致的一个 异步操作
总结
useState
的参数有两种格式
const [count, setCount] = useState(0)
const [count,setCount] = useState(()=>{ return 初始值})
setCount((上一次的值)=>{
return 新值
})
你真的学会了吗?
你真的学会了setXXX的参数为函数的使用场景了吗? 那我们来做个小小的题目吧
点击按钮, 让count 从60 开始倒计时
import React, { useState } from 'react'
import ReactDOM from 'react-dom'
export default function App () {
const [count, setCount] = useState(60)
const hClick = () => {
setInterval(() => {
setCount((count) => count - 1)
}, 1000)
}
return (
<div>
<h1>倒计时:{count}秒</h1>
<button onClick={hClick}>点击开始倒计时</button>
</div>
)
}
ReactDOM.render(<App/>, document.getElementById('root'))
注意事项
setXXX
参数要使用函数形式才能实现迭加, 不然你会发现,点击开始倒计时触发定时器后,只会输出一次 99 就没有任何变化了,其实定时器还在走,并没有停止,而是因为, 你没有是用函数式, 那么每次一秒后,定时器都会执行 setCount
,它拿到的永远不会是你上一次的值, 永远都是 100 的初始值 ,所以每次执行的都是 100 - 1
函数组件没有生命周期的概念 , 当然我这句话说的太绝对了 ,不是说没有 ,而是没有像 类组件中的生命周期钩子函数 , 但是在
在hooks的出现后 ,我们可以实现模仿类组件中的生命周期钩子函数
更新过程 - 示例代码
import React, { useState } from 'react'
import ReactDOM from 'react-dom'
export default function App () {
const [count, setCount] = useState(0)
return (
<div>
<h1>{count}</h1>
// 点击 count + 1
<button onClick={() => { setCount((count) => count + 1) }}>+1</button>
</div>
)
}
ReactDOM.render(<App/>, document.getElementById('root'))
函数组件使用 useState
hook 后的执行过程 , 以及状态值的变化:
useState(0)
将传入的参数作为状态的初始值. 即 : 0setCount((count)=>{count+1})
修改状态, 因为状态发生改变 ,所以该组件会重新渲染useState(0)
:
const [count, setCount] = useState(()=>{
console.log('useState...') // 这句只会输出一次
return 0
})
也就是说 , 以后的每次渲染 , useState
获取到都是最新的状态值 , React 组件会记住每次最新的状态值
总结
状态更新,整个组件的逻辑重新运行一次;
useState只会在组件第一次渲染时使用状态的初值,随后都会使用状态的最新值
useState 这个 Hook 就是用来管理 state 的,它可以让函数组件具有维持状态的能力。也就是说,在一个函数组件的多次渲染之间,这个 state 是共享的。
useState({状态1, 状态2 ..... })
useState(状态1) useState(状态2)
推荐使用方案二
调用 useState
hook 多次即可 , 每调用一次 useState hook 可以提供一个状态
注意:
hooks : setXXX(新值) ===> 用新值去替换之前的值
class: setState({要修改的属性})
useState
只能直接出现在 函数组件内部useState
不能嵌套在 if / for事物的主要作用之外的 , 就是副作用 ,记住这句话,要考的!!! 划重点
函数式组件
记住一句话 , 除了渲染 UI 以外的操作, 都可以称之为是 副作用 , 可以把业务逻辑写在 useEffect()
中 , 但不是所有的都必须
使用步骤
import React, { useEffect } from 'react'
import ReactDOM from 'react-dom'
export default function App () {
useEffect(() => {
console.log('1 ---useEffect执行了.....')
})
useEffect(() => {
console.log('2 ---useEffect执行了.....')
})
useEffect(() => {
console.log('3 ---useEffect执行了.....')
})
return (
<div>
</div>
)
}
ReactDOM.render(<App/>, document.getElementById('root'))
执行时机
在设置了依赖项后 , Effect
只有在依赖项 发生变化时, 才执行相应的 回调
示例代码
import React, { useState, useEffect } from 'react'
import ReactDom from 'react-dom'
export default function App () {
useEffect(() => {
console.log('useEffect,更新title')
document.title = 'count' + count
})
const [count, setCount] = useState(0)
const [n, setN] = useState(1)
return (
<div>
<h1>useEffect的依赖项</h1>
<button
onClick={() => {
setCount(count + 1)
}}>
点击了{count}次,会更新到文档的标题上
</button>
<div>
n:{n}
<button onClick={() => setN(n + 1)}>点击,让n+1</button>
</div>
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))
问题:
useEffect
都会执行第一次useEffect
执行 , 做了多余的操作useEffect
有两个参数:
参数一: 副作用函数
参数二: 执行副作用函数的依赖项 : 它决定了什么时机执行 参数一(副作用函数)
情况一: 不带第二个参数 , 执行时机 : 每次更新之后都要执行
情况二: 带第二个参数 , 参数是空数组, 执行时机: 页面刷新 第一次才执行 , 只执行一次
useEffect(() => {
// 副作用函数的内容
}, [依赖项])
使用场景:
因为这些操作 ,我们只需要执行一次 ,不需要它多次执行
情况三: 带第二个参数(数组格式) , 并指定了依赖项 , 执行时机: 1. 页面刷新加载初始执行一次 2. 依赖项的值发生了变化 ,执行一次
useEffect(() => {
// 副作用函数的内容
}, [依赖项1,依赖项2,....])