-
Hook
是16.8的新增内容,可以在不写 class
组件的情况下使用 state
及其他特性,比如状态变化、生命周期等,还可以在无需修改组件结构的情况下复用状态逻辑,可以将组件中相互关联的部分拆分成更小的函数,阅读体验更简洁
- 使用
Hook
要新建函数组件,并从 React
中引入 useState
方法,初始化结构如下:
// src/components/HookTest.js
import React, {useState} from 'react';
export default function HookTest () {
return (
<div></div>
)
}
- 使用
useState
实现一个计数器,记录点击按钮的次数
import React, {useState} from 'react';
export default function HookTest () {
// useState(initialState)是一个方法,该方法返回一个只包含2项的数组
// 解构数组后第0项表示状态,我们初始化了这个状态值为0,第1项是更新状态值的方法
const [count, setCount] = useState(0)
return (
<div>
<p>点击了{count}次</p>
<button onClick={() => {setCount(count + 1)}}>点击</button>
</div>
)
}
- 再举一个例子感受下
Hook
,当一个函数组件有多个状态时的使用,这里有一个水果列表,点击列表项即选中当前水果然后展示在页面上,同时还有一个输入框,可输入并点击“新增”按钮给水果列表新增水果项
// src/components/HookTest.js
import React, {useState} from 'react';
export default function HookTest () {
// useState(initialState)是一个方法,该方法返回一个数组
// 解构数组后第0项表示状态,我们初始化了这个状态值为0,第1项是更新状态值的方法
const [count, setCount] = useState(0)
// 页面展示的选中水果,默认初始值为'Lemon'
const [fruit, setFruit] = useState('Lemon')
// 输入框输入的内容,以及输入框onChange的操作
const [input, setInput] = useState('')
// 水果列表
const [fruits, setFruits] = useState(['apple', 'pear'])
return (
<div>
<p>点击了{count}次</p>
<button onClick={() => setCount(count + 1)}>点击</button>
<p>选择的水果:{fruit}</p>
<p>
<input type="text" value={input} onChange={(e) => {setInput(e.target.value)}}></input>
<button onClick={() => {setFruits([...fruits, input])}}>新增水果</button>
</p>
<ul>
{/* 点击列表项,将列表项内容展示在上面的选择的水果。。。 */}
{fruits.map(fruit => (<li key={fruit} onClick={() => setFruit(fruit)}>{fruit}</li>))}
</ul>
</div>
)
}
- 下面的例子,理解一个概念叫副作用钩子 -
Effect Hook
,可以将其理解为 componentDidMount
、componentDidUpdate
、componentWillUnmount
这几个生命周期的复合,可以理解为一个合成API;我们写一个功能当点击按钮记录点击次数时,同时修改浏览器当前标签页的标题,将标题也改为记录次数,如果我们用 class
组件做这个功能,要在好几个生命周期内执行标题更新的逻辑,但是现在有了 Effect Hook
就方便多了,使用前要先引入 useEffect
// src/components/HookTest.js
import React, {useState, useEffect} from 'react';
export default function HookTest () {
// useState(initialState)是一个方法,该方法返回一个数组
// 解构数组后第0项表示状态,我们初始化了这个状态值为0,第1项是更新状态值的方法
const [count, setCount] = useState(0)
// 每次渲染时都执行
// 可以理解为class类组件中的componentDidMount、componentDidUpdate、componentwillUnmount的合并api
useEffect (() => {
document.title = `你点击了${count}次`
})
return (
<div>
<p>点击了{count}次</p>
<button onClick={() => setCount(count + 1)}>点击</button>
</div>
)
}
- 上面例子展示了
useEffect
每次渲染都会执行,那在有些情况下这明显是不合理的,比如需要在 useEffect
里调用api时,这里就需要给 useEffect
传递第二个参数,第二个参数是一个依赖数组,也就是可以有同时有多个依赖
// src/components/HookTest.js
import React, {useState, useEffect} from 'react';
export default function HookTest () {
// useState(initialState)是一个方法,该方法返回一个数组
// 解构数组后第0项表示状态,我们初始化了这个状态值为0,第1项是更新状态值的方法
const [count, setCount] = useState(0)
// 每次渲染时都执行
// 可以理解为class类组件中的componentDidMount、componentDidUpdate、componentwillUnmount的合并api
useEffect (() => {
document.title = `你点击了${count}次`
})
// useEffect会在每次组件都渲染时调用,那么如果需要在此方法内部调用api时,频繁调用api就不合理了
// 这时可以给useEffect方法传递第二个参数,为[],表示没有任何依赖,只需要执行1次
useEffect (() => {
// ...api调用
console.log('api调用')
}, [])
// useEffect还可以根据依赖决定自身是否每次渲染都调用,如果依赖没有变,那渲染也是没有意义的
// 这时给useEffect方法传递第二个参数,传依赖,依赖count不变就不执行
useEffect (() => {
document.title = `你点击了${count}次`
}, [count])
return (
<div>
<p>点击了{count}次</p>
<button onClick={() => setCount(count + 1)}>点击</button>
</div>
)
}
- 除了以上用法,
Hook
中还有一个用法叫自定义钩子,他可以将某个 Hook
的逻辑抽离出来,以供多个组件复用,语法要求是以 use...
开头命名一个函数
// src/components/HookTest.js
import React, {useState, useEffect} from 'react';
// 自定义hook是个函数,名称用use开头即可,函数内部可以调用其他hook
function useAge () {
const [age, setAge] = useState(0)
useEffect(() => {
setTimeout(() => {
setAge(20)
}, 2000)
})
return age;
}
export default function HookTest () {
// 在需要使用的地方直接调用自定义钩子方法即可
const age = useAge()
return (
<div>
<p>年龄:{age ? age : 'loading...'}</p>
</div>
)
}
网友评论