hook不是正常的函数,使用hook需要遵循一定的规则
- Only Call Hooks at the Top Level
- Don’t call Hooks inside loops, conditions, or nested functions.
- Only Call Hooks from React Functions
- Don’t call Hooks from regular JavaScript functions. Instead, you can:
- Call Hooks from React function components.
- Call Hooks from custom Hooks
- Don’t call Hooks from regular JavaScript functions. Instead, you can:
create-react-app默认为Eslint安装了eslint-plugin-react-hooks插件,用于检测上述规则
custom hooks
自定义hook是常规的JS函数,只需要遵循hook规则,且必须以use开头
自定义Hook显然不仅是一种可重用的工具,它们还为将代码划分为更小的模块化部分提供了一种更好的方式。
在下面的App组件中多次使用useState, 都具有相同的解构
import React, { useState } from 'react'
const App = () => {
const [name, setName] = useState('')
const [born, setBorn] = useState('')
const [height, setHeight] = useState('')
return (
<div>
<form>
name:
<input
type="text"
value={name}
onChange={(event) => setName(event.target.value)}
/>
<br />
birthdate:
<input
type="date"
value={born}
onChange={(event) => setBorn(event.target.value)}
/>
<br />
height:
<input
type="number"
value={height}
onChange={(event) => setHeight(event.target.value)}
/>
</form>
<div>
{name} {born} {height}
</div>
</div>
)
}
export default App
抽离成自定义hook,优点是可以重用
import React, { useState } from 'react'
// 自定义hook
const useField = (type) => {
const [value, setValue] = useState('')
const onChange = (event) => {
setValue(event.target.value)
}
return {
type,
value,
onChange,
}
}
const App = () => {
const name = useField('text')
const born = useField('date')
const height = useField('number')
return (
<div>
<form>
name:
<input type={name.type} value={name.value} onChange={name.onChange} />
<br />
birthdate:
<input type={born.type} value={born.value} onChange={born.onChange} />
<br />
height:
<input
type={height.type}
value={height.value}
onChange={height.onChange}
/>
</form>
<div>
{name.value} {born.value} {height.value}
</div>
</div>
)
}
export default App
因为name对象的属性名与input期望的props属性名一一对应,因此可以使用展开属性
<form>
name: <input {...name} />
<br />
birthdate: <input {...born} />
<br />
height: <input {...height} />
</form>
input元素的type属性有多少种?
互联网上开始充斥着越来越多关于Hook的有用资料。 如下是值得一查的资料来源:
- Awesome React Hooks Resources
- Easy to understand React Hook recipes by Gabe Ragland
- Why Do React Hooks Rely on Call Order?
Exercises
将自定义hook写在**src/hooks/index.js**
文件里, 使用具名导出
import { useState } from 'react'
export const useField = (type) => {
const [value, setValue] = useState('')
const onChange = (event) => setValue(event.target.value)
return {
type,
value,
onChange,
}
}
导入
import { useField } from './hooks'
Reset
想用reset元素清空表单,发现没有效果,这是因为reset原理是把表单还原成默认的value值,而不是清空,
如果默认的value值不为空,则无法达到清空的目的
<form>
<input type="text" value="xxx"/>
<input type="password" value="hahah"/>
<input type="reset" /> <!-- 清空无效 -->
</form>
点击form中的button触发submit
解决办法1:给button点击事件添加**event.preventDefault()**
阻止默认事件,或者添加**event.stopPropagation()**
阻止冒泡
解决办法2:给button加上属性**type="button"**
如果展开运算有冲突,将要展开的元素单独放一个对象
import { useState } from 'react'
export const useField = (type, name) => {
const [value, setValue] = useState('')
const onChange = (event) => setValue(event.target.value)
const reset = () => setValue('')
return {
props: {
type,
name,
value,
onChange,
},
reset,
}
}
const content = useField('text')
// ...
<div>
content
<input {...content.props} />
</div>
exercise 7.7
使用api获取信息,使用catch处理错误
const useCountry = (name) => {
const [country, setCountry] = useState(null)
useEffect(() => {
if (name) {
const queryUrl = `https://restcountries.eu/rest/v2/name/${name}?fullText=true`
axios
.get(queryUrl)
.then((response) => {
setCountry({ found: true, data: response.data[0] })
})
.catch(() => {
setCountry({ found: false })
})
}
}, [name])
return country
}
exercise 7.8
将与后端通信写到自定义hook中
import { useEffect, useState } from 'react'
import axios from 'axios'
export const useField = (type) => {
const [value, setValue] = useState('')
const onChange = (event) => {
setValue(event.target.value)
}
return {
type,
value,
onChange,
}
}
export const useResource = (baseUrl) => {
const [resources, setResources] = useState([])
useEffect(() => {
axios
.get(baseUrl)
.then((response) => {
setResources(response.data)
})
.catch((error) => console.log(error))
}, [baseUrl])
const create = (resource) => {
axios
.post(baseUrl, resource)
.then((response) => setResources(resources.concat(response.data)))
.catch((error) => console.log(error))
}
const service = {
create,
}
return [resources, service]
}