什么是钩子(hooks)?
- 消息处理的一种方法,用来监视指定程序
- 函数组件中需要处理副作用,可以用钩子把外部代码”钩”进来
- Hooks的目的是为了给函数式组件加上状态
-
useState
const App: React.FC = (props) => {const [count, setCount] = useState(0)return (<div className={styles.app}><buttononClick={() => {setCount(count + 1)}}>{count}</button></div>)}
setCount是异步的,即使有多个setCount也只会执行一次
sideEffect
纯函数:
- 函数与外界的只有唯一一个渠道进行沟通,通过传入参数和返回值进行沟通。
- 相同的传入参数永远只会有相同的返回值。
副作用:
- 除了返回值,还做了其他事情,例如修改全局变量
useEffect
- 如果不传第二个参数,每次渲染都会调用useEffect, 相当于componentDidUpdate
- 第二个参数传[], 相当于componentDidMount
useEffect可以return一个清理函数,相当于componentWillUnmount时调用
function Example() {const [count, setCount] = useState(0);useEffect(() => {document.title = `You clicked ${count} times`;});return (<div><p>You clicked {count} times</p><button onClick={() => setCount(count + 1)}>Click me</button></div>);}
在useEffect中使用async/await
直接将回调函数写成async/await是不行的,因为async默认返回的类型是Promise,与useEffect冲突

正确写法是先声明一个函数再调用useEffect(() => {const getRobotGallery = async () => {const response = await fetch('https://jsonplaceholder.typicode.com/users')const data = await response.json()setRobotGallery(data)}getRobotGallery()}, [])
处理loading
声明一个loading标记
const [loading, setLoading] = useState<boolean>(false)useEffect(() => {const fetchData = async () => {setLoading(true)const response = await fetch('https://jsonplaceholder.typicode.com/users')const data = await response.json()setRobotGallery(data)setLoading(false)}fetchData()}, [])
根据标记显示
{!loading ? (<div className={styles.robotList}>{robotGallery.map((r: any) => (<Robot id={r.id} name={r.name} email={r.email} />))}</div>) : (<div>loading 数据加载中...</div>)}
处理error
用try…catch
const [errorMsg, setErrorMsg] = useState('')useEffect(() => {const fetchData = async () => {setLoading(true)try {const response = await fetch('https://jsonplaceholder.typicode.com/users')const data = await response.json()setRobotGallery(data)} catch (e: any) {setErrorMsg(e.message)}setLoading(false)}fetchData()}, [])
context和useContext
context可以避免数据通过props层层传递
在index.tsx中注册context ```jsx const defaultContextValue = { name: ‘jack’, } export const appContext = React.createContext(defaultContextValue) // 参数是默认值
ReactDOM.render(
在组件中消费context, comsumer必须使用函数形式传递value```jsximport { appContext } from '../index'const Robot: React.FC<RobotProp> = ({ id, name, email }) => {return (<appContext.Consumer>{(value) => (<div className={styles.cardContainer}><img src={`https://robohash.org/${id}`} alt="robot" /><h2>{name}</h2><p>{email}</p><p>作者:{value.name}</p></div>)}</appContext.Consumer>)}
用useContext代替consumer
import { appContext } from '../index'import { useContext } from 'react'const Robot: React.FC<RobotProp> = ({ id, name, email }) => {const user = useContext(appContext)return (<div className={styles.cardContainer}><img src={`https://robohash.org/${id}`} alt="robot" /><h2>{name}</h2><p>{email}</p><p>作者:{user.name}</p></div>)}
context是局部的全局变量
useContext可以和useReducer或者useState配合,将reducer或者state传递到任意组件
在class组件中只能使用context.consumer
封装context provider组件
import React, { useState } from 'react'interface DefaultContextValue {name: stringshoppingCartItems: { id: number; name: string }[]}const defaultContextValue: DefaultContextValue = {name: 'jack',shoppingCartItems: [],}interface StateContext {state: DefaultContextValuesetState:| React.Dispatch<React.SetStateAction<DefaultContextValue>>| undefined}export const appContext = React.createContext<StateContext>({state: defaultContextValue,setState: undefined,})export const AppStateProvider: React.FC = (props) => {const [state, setState] = useState(defaultContextValue)return (<appContext.Provider value={{ state, setState }}>{props.children}</appContext.Provider>)}
这里为了把state和setState作为一个对象传给一个context,用typescript才这样写
也可以把state和setState分别注册成context, 使用2个provider
import React, { useState } from 'react'interface DefaultContextValue {name: stringshoppingCartItems: { id: number; name: string }[]}const defaultContextValue: DefaultContextValue = {name: 'jack',shoppingCartItems: [],}export const appContext = React.createContext(defaultContextValue)export const appSetStateContext = React.createContext<React.Dispatch<React.SetStateAction<DefaultContextValue>>| undefined>(undefined)export const AppStateProvider: React.FC = (props) => {const [state, setState] = useState(defaultContextValue)return (<appContext.Provider value={state}><appSetStateContext.Provider value={setState}>{props.children}</appSetStateContext.Provider></appContext.Provider>)}
实现加入购入车功能
const Robot: React.FC<RobotProp> = ({ id, name, email }) => {const store = useContext(appContext)const addToCart = () => {if (store.setState) {store.setState({...store.state,shoppingCartItems: [...store.state.shoppingCartItems, { id, name }],})}}return (<div className={styles.cardContainer}><img src={`https://robohash.org/${id}`} alt="robot" /><h2>{name}</h2><p>{email}</p><p>作者:{store.state.name}</p><button onClick={addToCart}>加入购物车</button></div>)}
高阶组件Higher-Order Components(HOC)
高阶组件是参数为组件,返回值为新组件的函数。高阶组件都以with开头
如果两个组件有相似的代码逻辑,应使用高阶组件封装,提高代码复用率
文档
可以返回函数式组件或者类组件
import { appContext } from '../AppState'import { useContext } from 'react'import { RobotProp } from './Robot'export const withAddToCart = (ChildComponent: React.ComponentType<RobotProp>) => {// return class extends React.Component {}return (props: any) => {const store = useContext(appContext)const addToCart = (id: number, name: string) => {if (store.setState) {store.setState({...store.state,shoppingCartItems: [...store.state.shoppingCartItems, { id, name }],})}}return <ChildComponent {...props} addToCart={addToCart} store={store} />}}
使用
import styles from './Robot.module.css'import { StateContext } from '../AppState'import { withAddToCart } from './AddToCart'export interface RobotProp {id: numbername: stringemail: stringstore: StateContextaddToCart: (id: number, name: string) => void}const Robot: React.FC<RobotProp> = ({ id, name, email, store, addToCart }) => {return (<div className={styles.cardContainer}><img src={`https://robohash.org/${id}`} alt="robot" /><h2>{name}</h2><p>{email}</p><p>作者:{store.state.name}</p><button onClick={() => addToCart(id, name)}>加入购物车</button></div>)}export default withAddToCart(Robot)
自定义hook
自定义 Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook。
自定义hook可以代替HOC高阶组件
export const useAddToCart = () => {const store = useContext(appContext)const addToCart = (id: number, name: string) => {if (store.setState) {store.setState({...store.state,shoppingCartItems: [...store.state.shoppingCartItems, { id, name }],})}}return {store,addToCart,}}
使用
import styles from './Robot.module.css'import { useAddToCart } from './AddToCart'export interface RobotProp {id: numbername: stringemail: string}const Robot: React.FC<RobotProp> = ({ id, name, email }) => {const { store, addToCart } = useAddToCart()return (<div className={styles.cardContainer}><img src={`https://robohash.org/${id}`} alt="robot" /><h2>{name}</h2><p>{email}</p><p>作者:{store.state.name}</p><button onClick={() => addToCart(id, name)}>加入购物车</button></div>)}export default Robot
