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:<inputtype="text"value={name}onChange={(event) => setName(event.target.value)}/><br />birthdate:<inputtype="date"value={born}onChange={(event) => setBorn(event.target.value)}/><br />height:<inputtype="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'// 自定义hookconst 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:<inputtype={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]}
