让你在不编写class的情况下使用state以及其他的React特性。
Hooks的本质:一套能够使函数组件更强大,更灵活的“钩子”
React体系里组件分为 类组件 和 函数组件
函数组件是有利于逻辑拆分与重用的组件表达形式。而先前的函数组件是不可以有自己的状态的,为了能让函数组件可以拥有自己的状态,所以从react v16.8开始,Hooks应运而生。
注意点:
Hooks的出现解决了2个问题 1. 组件的状态逻辑复用 2.class组件自身的问题
组件的逻辑复用
在hooks出现之前,react先后尝试了 mixins混入,HOC高阶组件,render-props等模式,但是都有各自的问题,比如mixin的数据来源不清晰,高阶组件的嵌套问题等等
class组件自身的问题
class组件大而全,提供了很多东西,有不可忽视的学习成本,比如各种生命周期,this指向问题等等,而我们更多时候需要的是一个轻快灵活的组件。
useState为函数组件提供状态(state)。
useState
函数·useState
函数,并传入状态的初始值(必须在函数组件中)useState
函数的返回值中,拿到状态和修改状态的方法返回值为:当前state以及更新state的函数
- import { useState } from 'react'
-
- function App() {
- // 参数:状态初始值比如,传入 0 表示该状态的初始值为 0
- // 返回值:数组,包含两个值:1 状态值(state) 2 修改该状态的函数(setState)
- const [count, setCount] = useState(0)
- return (
- <button onClick={() => { setCount(count + 1) }}>{count}button>
- )
- }
- export default App
点击按钮后,会看见state改变
该方式提供的状态,是函数内部的局部变量,可以在函数内的任意位置使用
最新的状态值
1.userState传过来的参数,作为count的初始值
2.[count,setCount]:是一个解构赋值,useState返回值是一个数组
3.名字可以自定义,保持语义化
4.顺序不可以换,第一参数是数据状态,第二参数是修改数据的方法
5.setCount函数:用来修改count,依然保持不能直接修改原值,还是生成一个新值替换原值
setCount(基于原值计算得到的新值)
6.count和setCount是一对:是绑在一起的setCount只能用于修改对应的count值
- import { useState } from 'react'
-
- function App() {
- const [count, setCount] = useState(0)
- return (
- <button onClick={() => { setCount(count + 1) }}>{count}button>
- )
- }
- export default App
修改状态的时候,一定要使用新的状态替换旧的状态,不能直接修改旧的状态,尤其是引用类型
函数组件使用 useState hook 后的执行过程,以及状态值的变化
useState(0)
将传入的参数作为状态初始值,即:0setCount(count + 1)
修改状态,因为状态发生改变,所以,该组件会重新渲染useState(0)
,此时 React 内部会拿到最新的状态值而非初始值,比如,该案例中最新的状态值为 1注意:useState 的初始值(参数)只会在组件第一次渲染时生效。也就是说,以后的每次渲染,useState 获取到都是最新的状态值,React 组件会记住每次最新的状态值
- import { useState } from 'react'
- // 首次渲染:组件内部的代码会被执行一次
- // 更新渲染:setCount都会更新:
- // 1.app组件会再次渲染,这个函数会再次执行
- // 2.useState再次执行,得到的是新的count值,不是0,而是修改后的1
- // 模板会用新值渲染
- function App() {
- // count:数据状态
- // setCount:修改count的函数(专有函数)
- const [count, setCount] = useState(0)
- // 在这里可以进行打印测试
- console.log(count)
- return (
- <button onClick={() => { setCount(count + 1) }}>{count}button>
- )
- }
- export default App
每次执行互相独立,每调用一次为函数组件提供一个状态
- function List(){
- // 以字符串为初始值
- const [name, setName] = useState('cp')
- // 以数组为初始值
- const [list,setList] = useState([])
- }
1.只能出现在函数组件中
2.不能嵌套在if/for/其它函数中(react按照hooks的调用顺序识别每一个hook)
- //错误写法
- let num = 1
- function List(){
- num++
- if(num / 2 === 0){
- const [name, setName] = useState('cp')
- }
- const [list,setList] = useState([])
- }
- // 俩个hook的顺序不是固定的,这是不可以的!!!
3.可以通过开发者工具component查看hooks状态
4.只能在React的函数组件中调用Hook,不要在其他的js函数中调用(还有自定义Hook可以调用Hook)
useEffect函数的作用就是为react函数组件提供副作用处理的。
useEffect
就是一个 Effect Hook,给函数组件增加了操作副作用的能力。componentDidMount
、componentDidUpdate
和 componentWillUnmount
具有相同的用途,只不过被合并成了一个 API。1.作用
为react函数组件提供副作用处理
useEffect
函数useEffect
函数,并传入回调函数在修改数据后,把count值放到页面标题中
- import { useEffect, useState } from 'react'
-
- function App() {
- const [count, setCount] = useState(0)
-
- useEffect(()=>{
- // dom操作
- // 定义副作用
- document.title = `当前已点击了${count}次`
- document.title=count
- })
- return (
- <button onClick={() => { setCount(count + 1) }}>{count}button>
- )
- }
-
- export default App
定义state变量count,让页面的标题为count,并且随着count一起变化
使用类组件
- class App extends React.Component {
- state = { count: 0 }
- changeCount = () => {
- this.setState({ count: this.state.count + 1 })
- }
- componentDidMount () {
- document.title = this.state.count
- }
- componentDidUpdate () {
- document.title = this.state.count
- }
- render () {
- return (
- <div>
- <h2>{this.state.count}h2>
- <button onClick={this.changeCount}>count+1button>
- div>
- )}}
点击+1后,页面的值变了,但是页面标题没有改变。跟生命周期有关 ,因为生命周期只会执行一次,在页面渲染完毕后,componentDidMount会执行一次,然后就不会执行了,标题就一直等于0
所以,在页面改变后,还要触发一个生命周期函数componentDidUpdate
- //当页面发生更新后,就会触发这个生命函数
- componentDidUpdate () {
- document.title = this.state.count
- }
在class中,需要在两个生命周期函数中编写重复的代码, 会臃肿。
使用函数组件
- import { useEffect, useState } from 'react'
-
- function App () {
- const [count, setCount] = useState(0)
- //组件在首次渲染的时候执行一次
- //当组件修改状态更新组件的时候,副作用不断执行
- useEffect(() => {
- console.log(111)
- // 定义副作用
- document.title = count
- })
- return (
- <div>
- <h2>{count}h2>
- <button onClick={() => { setCount(count + 1) }}>{count}button>
- div>
- )}
会执行两次,因为
页面首次渲染执行一次,然后每次更新都会执行。
组件首次渲染执行一次,以及不管是哪个状态更改引起组件更新时都会重新执行
- useEffect(()=>{
- console.log('副作用执行了')
- })
组件只在首次渲染时执行一次。
- useEffect(()=>{
- console.log('副作用执行了')
- },[])
副作用函数在首次渲染时执行,在依赖项发生变化时重新执行
添加特定依赖项, 依赖项改变多少次,则执行多少次。
- function App() {
- const [count, setCount] = useState(0)
- const [name, setName] = useState('zs')
-
- useEffect(() => {
- console.log('副作用执行了')
- }, [count])
-
- return (
- <>
- <button onClick={() => { setCount(count + 1) }}>{count}button>
- <button onClick={() => { setName('cp') }}>{name}button>
- >
- )
- }
useEffect 回调函数中用到的数据(比如,count)就是依赖数据,就应该出现在依赖项数组中,如果不添加依赖项就会有bug出现
- // 有count和name,所以都要添加到依赖项数组中
- useEffect(() => {
- // dom操作
- // 定义副作用
- document.title = `当前已点击了${count}次`
- document.title = count
- console.log(name)
-
- }, [count, name])
- // 此时:初始化+count/name被修改时都会执行副作用函数
某种意义上,hook的出现,就是想不用生命周期概念也可以写业务代码
依赖项:控制副作用的执行时机
1.默认状态(无依赖项)
组件初始化时,先执行一次,等到每次数据修改组件更新再次执行(后续组件更新几次执行几次)
2.添加一个空数组依赖项
组件初始化的时候,执行一次,后续不再执行。
3.依赖特定项
组件初始化时,执行一次,依赖的特定项发送变化会再次执行 (后续依赖项改变几次执行几次)
需求描述**:自定义一个hook函数,实现获取滚动距离Y
const [y] = useWindowScroll()
1.创建hooks/useWindowScroll.js,填入以下内容
- import { useState } from "react"
-
- export function useWindowScroll () {
- // geuy赋值,有固定的写法:setY
- const [y, setY] = useState(0)
- // 在滚动行为发生时,不断获取滚动值,然后交给y
- window.addEventListener('scroll', () => {
- const h = document.documentElement.scrollTop
- setY(h)
- })
- return [y]
- }
2.在App.js中使用此函数
- import { useWindowScroll } from './hooks/useWindowSroll'
-
- function App () {
- const [y] = useWindowScroll()
- return (
- <div style={{ height: '12000px' }}>
- {y}
- div>
- )
- }
-
- export default App
需求描述: 自定义hook函数,可以自动同步到本地LocalStorage
const [message, setMessage] = useLocalStorage(key,defaultValue)
- message可以通过自定义传入默认初始值
- 每次修改message数据的时候 都会自动往本地同步一份
1.创建useLocalStorage.js
- import { useEffect, useState } from 'react'
-
- export function useLocalStorage (key, defaultValue) {
- const [message, setMessage] = useState(defaultValue)
- // 每次只要message变化 就会自动同步到本地ls
- useEffect(() => {
- // 当数据变化时,往里面存入一份
- window.localStorage.setItem(key, message)
- }, [message, key])
- return [message, setMessage]
- }
2.导入到App.js中使用
- import { useWindowScroll } from './hooks/useWindowSroll'
- import { useLocalStorage } from './hooks/useLocalStorage'
- function App () {
- const [y] = useWindowScroll()
- const [message, setMessage] = useLocalStorage('hook-key', 'zz')
- // 5秒后。从zz变成cp->Application的Key
- setTimeout(() => {
- setMessage('cp')
- }, 5000)
- return (
- <div style={{ height: '12000px' }}>
- {y}
- {message}
- div>
- )
- }
-
- export default App