一、简介
hooks
的本质:一套能够使函数组件更强大,更灵活的 “钩子”。React
体系里组件分为类组件
和函数组件
。
- 经过多年的实战,函数组件是一个更加匹配
React
的设计理念UI = f(data)
,也更有利于逻辑拆分与重用的组件表达形式,而先前的函数组件是不可以有自己的状态的,为了能让函数组件可以拥有自己的状态,所以从react v16.8
开始,Hooks
应运而生。 - 有了
hooks
之后,为了兼容老版本,class
类组件并没有被移除,俩者都可以使用。 - 有了
hooks
之后,不能在把函数成为无状态组件了,因为hooks
为函数组件提供了状态。 hooks
只能在函数组件或者自定义hook
中使用,不能嵌套
在if/for/其他函数中
(react
会按照hooks
的调用顺序识别每个hook
)。
hooks
解决了什么问题?
- 组件的逻辑复用
在hooks
出现之前,react
先后尝试了mixins
混入,HOC
高阶组件,render-props
等模式,但是都有各自的问题,比如mixin
的数据来源不清晰,高阶组件的嵌套问题等等 class
组件自身的问题class
组件就像一个厚重的战舰
一样,大而全,提供了很多东西,有不可忽视的学习成本,比如各种生命周期,this
指向问题等等,而更多时候需要的是一个轻快灵活的快艇
。
hooks
让函数组件拥有了类组件的特性,例如组件内的状态、生命周期等,常用的有:
useState
useEffect
- useRef 详细使用
- useContext 详细使用
- …
二、useState
使用
- 基础案例
import React, { useState } from "react"; // 函数组件 function Sub () { // 参数:状态初始值(传入 0 表示该状态的初始值为 0) // 返回值:数组(包含两个值:1、状态值(state)2、修改该状态的函数(setState)) // 状态值名字可以自定义吗?可以自定义保持语义化 // 返回值的顺序是否可以替换?不可以,第一个就是数据状态,第二个就是修改数据的函数 // 组件的更新过程 与 当调用 setCount 的时更新过程: // 》首次渲染 // 首次被渲染的时候,组件内部的代码会执行一次 // 其中 useState 也会跟着执行,这里重点注意,初始值只在首次渲染时生效 // 》更新渲染,调用 setCount 整个 app 中代码都会更新 // 1、app 组件会再次渲染,这个函数会再次执行 // 2、useState 再次执行,得到新的 count 值不是 0 而是修改之后的 1,模版会用新值渲染 const [count, setCount] = useState(0) // 可以通过输出 count 验证 console.log(count) // useState 可以执行多次 const [list, setList] = useState([]) // 点击方法 function touchButton () { // 函数中不能写,会报错的 // const [flag, setFlag] = useState(true) // + 1 setCount(count + 1) } return ( <button onClick={touchButton}>{count}</button> // <button onClick={() => setCount(count + 1)}>{count}</button> ) } class App extends React.Component { render () { return ( <div> <Sub></Sub> </div> ) } } export default App
- 函数方式
import React, { useState } from "react"; function getDefaultValue () { // 耗时操作 for (let index = 0; index < 20000; index++) { } // 返回结果 return 10 } // 函数组件 function Sub (props) { const [count, setCount] = useState(() => { // return props.count return getDefaultValue() }) return ( <button onClick={() => setCount(count + 1)}>{count}</button> ) } class App extends React.Component { render () { return ( <div> <Sub count={10}></Sub> </div> ) } } export default App
二、useEffect
使用
- 理解函数副作用,什么是副作用?
副作用是相对于主作用来说的,一个函数除了主作用,其他的作用就是副作用。对于React
组件来说,主作用就是根据数据(state/props)渲染 UI,除此之外都是副作用。 - 常见的副作用
- 数据请求
ajax
发送 - 手动修改
dom
localstorage
操作- …
useEffect
函数的作用就是为react
函数组件提供副作用处理的!
import React, { useEffect, useState } from "react"; // 函数组件 function Sub () { const [count, setCount] = useState(0) // 例如:count 的主作用就是更换按钮中的内容,但是当需要用到它的副作用时,需要在 useEffect 函数中实现它副作用效果 useEffect(() => { console.log('副作用函数调用了') // 副作业函数会每次数据变化都执行,默认是全部属性的副作用都会调用该函数,可以指定 // 例如:当它初始化或变化时设置为网页标题,这就是副作业 document.title = count }) return ( // 这是 count 的主作用 <button onClick={() => setCount(count + 1)}>{count}</button> ) } class App extends React.Component { render () { return ( <div> <Sub></Sub> </div> ) } } export default App
useEffect(() => { console.log('副作用函数调用了') document.title = count }, [])
// 函数组件 function Sub () { const [count, setCount] = useState(0) const [name, setName] = useState('dzm') // 指定 count/name 会有副作用回调,首次渲染也会回调 useEffect(() => { console.log('副作用函数调用了') document.title = count // 此时什么时候会执行副作用函数?初始化 + count/name被修改时都会执行 }, [count, name]) // 可以写多个副作用函数 // useEffect(() => { // console.log('count 副作用函数调用了') // }, [count]) // useEffect(() => { // console.log('name 副作用函数调用了') // }, [name]) return ( <> <button onClick={() => setCount(count + 1)}>{count}</button> <button onClick={() => setName('test')}>{name}</button> </> ) }
- 注意:
useEffect
回调函数中用到的数据(比如:count
),也就是依赖数据,就应该出现在依赖项数组中,如果不添加依赖项会有bug
出现,就算不是通过useState
创建的属性也需要添加到数组中。 - 清理副作用使用场景:在组件被销毁时,如果有些副作用操作需要被清理,就可以使用这种方式(比如常见的
定时器
)。如果想要清理副作用,可以在副作用函数中的末尾return
一个新的函数,在新的函数中编写清理副作用的逻辑,注意执行时机为:
- 组件卸载时自动执行
- 组件更新时,下一个
useEffect
副作用函数执行之前自动执行
import { useEffect, useState } from "react" const App = () => { const [count, setCount] = useState(0) useEffect(() => { const timer = setInterval(() => { setCount(count + 1) }, 1000) return () => { // 用来清理副作用的事情 clearInterval(timer) } }, [count]) return ( <div>{count}</div> ) } export default App