Hooks 是一项新功能提案,可以在不编写类的情况下使用 state(状态)和其他 react 功能
使用 hooks 的理由
- 高阶组件为了复用,会导致代码层级复杂
- 类声明的组件中,生命周期复杂,代码编写容易混乱
- 写成函数式组件,是无状态组件;因为需要状态,又得改成 class 组件,开发成本变高
hooks 的使用
-
useState() 保存组件状态
/* 语法:const [state, setstate] = useState(initState),可以定义多个状态 state:状态名 setstate:改变对应状态的方法 initState:状态的初始值 */ import React, { useState } from 'react' export default function App (props) { const [stateA, setstateA] = useState(true) return ( <div> 状态:{ stateA } <button onClick = { ()=> setstateA(false) }>修改状态</button> </div> ) }
-
useRef() 保存引用值
import React, { useRef } from 'react' export default function App (props) { /** 创建ref的方式有两种: const ref1 = createRef() const ref2 = useRef() 在第初始化阶段,使用 createRef 与 useRef 两者是没有区别。但是在第更新阶段两者是有区别的。 1. 我们使用一个外部的变量store来存储初次所创建的ref,在我们对组件进行更新后,更新后的ref与我们初次创建的ref其实并不一致。这样也就意味着我们每更新一次组件, 就重新创建一次ref。 2. 使用 useRef 创建的 ref 仿佛就像外部定义的一个全局变量,不会随着组件的更新而重新创建。但组件销毁,它也会消失,不用手动进行销毁。 */ cosnt myInput = useRef('') return ( <div> input 的 value值:{ myInput.current.target.value } <input type='text' ref={ myInput } /> </div> ) }
-
useCallback() 记忆函数
- 防止因为组件重新渲染,而导致方法被重新创建,起到缓存作用,只有第二个参数发生改变才会重新声明一次函数
/* 语法:let handleClick = useCallback(fn, [依赖项]) 第一个参数事件处理函数 第二个参数:依赖项,数组形式 1. 如果第二个参数不传,每次数据更新都会重新声明这个函数 2. 如果第二个参数传入空数组,那么函数中的数据就会被缓存, 即使数据改变,函数中的永远的原来的数据 3. 如果第二个参数传入对应依赖项,只有依赖项改变后,这个函数才会重新声明一次 */ import React, { useState, useCallback } from 'react' export default function App (props) { const [name, setname] = useState('小明') let handleClick = useCallback(() => { // 只有 name 改变,才会重新声明这个函数 console.log(name) }, [name]) return ( <div> <button onClick = { handleClick }>click</button> </div> ) }
-
useEffect() 处理副作用 和 useLayoutEffect() 同步执行副作用
- 函数式组件中不存在生命周期,而使用上述方法可以处理对应的业务逻辑
- 如果明明使用某个变量,却没有申明在依赖中,当依赖改变时,useEffect 也不会再执行了,且 eslint 会报警告
- useLayoutEffect 与 useEffect 用法一样,区别是一个同步执行,一个异步执行
/* 语法:useEffect(fn, [依赖项]) 第一个参数事件处理函数 注意:该参数如果返回一个回调函数,则这个回调函数会在该组件即将销毁时调用:可以用来做事件解绑、清除定时器操作 第二个参数:依赖项,数组形式 1. 如果第二个参数不传,每次数据更新都会重新执行这个函数 2. 如果第二个参数传入空数组,那么这个函数只有在初始化的时候执行一次 3. 如果第二个参数传入对应依赖项,那么初始化和依赖项改变时都会执行这个函数 */ import React, { useState, useEffect } from 'react' import axios from 'axios' export default function App (props) { const [list, setlist] = useState([]) const id = props.match.params.id useEffect(() => { axios.get(`http://loaclhost:5000/user/${id}`).then(res => { setlist(res.data) }) }, [id]) return ( <div> <p>app</p> </div> ) }
区别:useLayoutEffect 是同步执行的,会在 dom 更新完成后立即执行,但是会在浏览器进行热歌绘制之前运行完成,阻塞了浏览器的绘制,跟 class 写法的 componentDidMount 和 componentDidUpdata 类似,都是同步执行
useEffect 是异步执行的,不会阻塞浏览器绘制,性能会更好一点
-
useReducer 和 useContext 减少组件层级
- 全局状态
import React, { useReducer} from 'react' // 利用 context 状态树全局通信 const GlobalContext = React.createContext() // reducer 可以抽离出去 const reducer = (prevState, action) => { const newState = {...prevState} switch(action.type) { case 'set_isShow' : newState.isShow = action.payload return newState default : return prevState } } export default function App () { const [state, dispatch] = useReducer(reducer, {isShow: ture}) return ( // 把 state, dispatch 传给所有子组件 <GlobalContext.Provider value = {{state, dispatch}}> <div> { state.isShow } </div> </GlobalContext.Provider> ) }
- 子组件中接收
import React, { useContext } from 'react' export default function Son () { // 通过 useContext 获取根组件传递的数据 const {state, dispatch} = useContext.(GlobalContext) return ( <div> { state.isShow } </div> ) }
自定义 hooks
- 当我们想在两个函数之间共享逻辑,或者想抽离出组件内的 hooks 时,我们可以把它们提到单独的函数中
- 自定义 hooks 的函数名必须是以 use 开头 ,否则会报错
import React, { useState, useEffect } from 'react' import axios from 'axios' // 自定义的 hooks function useGetList (id) { const [list, setlist] = useState([]) useEffect(() => { axios.get(`http://loaclhost:5000/user/${id}`).then(res => { setlist(res.data) }) }, [id]) return list } export default function App (props) { const id = props.match.params.id const list = useGetList(id) return ( <div> <p>app</p> </div> ) }