ReactHook是什么?
对函数型组件进行增强,让函数型组件可以储存状态,可以拥有处理副作用的能力。让开发者在不使用类组件的情况下,实现相同的功能。相比之下ReactHook比类组件使用更加容易上手,逻辑更加容易复用,也避免很多this指向不明确的问题。
扩展: (函数副作用的含义: 在I/O模型中,我们希望在在I到O之间只有计算,如果中间包含且不仅包含触发了其他I/O、与此次I -> O计算并不相关的任何事情,都称为副作用。典型例子:1.与外界交互的;2.调用I/O的;3.从函数范围之外检索值;4.磁盘检索;5.抛出异常)
React Hooks 的钩子函数
useState => 原本函数型组件在使用完后,内部的变量就会被释放掉。而这个钩子通过闭包可以让函数型组件可以保存状态,保存内部的变量。注意:1、设置状态值方法的参数,可以是一个值也可以是一个函数。2、useState 设置状态值方 是异步的。3、初始值可以是任意数据类型的值,也可以是个函数返回值(动态设置初始值)。
useReducer => 另外一种让函数保存状态的形式。适用于state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等
function reducer(state,action){
swich(action.type){
case "decrement"
return state - 1
}
}
const [count, dispatch] = useReducer(reducer,0)
<button onClick = {() => {dispatch({type:"decrement"})}> -1 <button>
useContext => 简化跨组件层级获取数据代码
useEffect => 拥有处理副作用的能力,类似组件生命周期函数
执行时机:
相比类组件生命周期的优点:可以多次调用,区分不同业务逻辑代码。
结合异步函数:useEffect里面的函数不能直接是异步函数,因为useEffect 默认返回清理函数的函数,而异步函数会返回Promise对象。故使用函数自执行的方法来实现包装这个异步函数。
useEffect (()=>{(async function() { await xxx })()},[] )
useMemo => 类似Vue 的计算属性,检测某个值的变化,根据变化值,计算新值。但是useMemo 会缓存计算结果,如果检测值没有变化,即使组件从新渲染,也不会重新计算,此方法有助于避免每次渲染进行昂贵的计算。
memo => 性能优化,如果本组件的数据没有变化,阻止组件更新。类似类组件的shouldComponentUpdate 和 pureComponent
useCallback => 性能优化,缓存函数,使组件重新渲染时得到相同的函数实例。尤其是对子组件传递函数方法的时候,使用该方法。
useRef => 1.获取Dom元素;2.保存数据,即使组件从新渲染,保存的数据还在,保存的数据被更改不会触发组件重新渲染。能够跨生命周期保存数据。
自定义Hooks => 标准的封装和共享逻辑的函数,以use开头。
部分钩子函数的原理实现
useState, useEffect
import ReactDOM from "react-dom"
let state = []
let setters = []
let stateIndex = 0
let effectIndex = 0
function render () {
stateIndex = 0
effectIndex = 0
ReactDOM.render(<App/>,document.getElementById("root"))
}
function createSetter(index){
return function setState(newState){
state[index] = newState
render()
}
}
function useState(initialState){
state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState
setters.push(createSetter(stateIndex))
let value = state[stateIndex]
let setter = setters[stateIndex]
stateIndex++
return [value,setter]
}
let preDepsAry = []
function useEffect (callback,depsAry){
// 判断callback 是否是函数
if(Object.prototype.toString.call(callback) !== '[object Function]') throw new Error("useEffect 函数的第一个参数必须是函数")
// 判断depsAry有没有传递
if(typeof depsAry === "undefined"){
callback()
}else {
// 判断是不是数组
if(Object.prototype.toString.call(depsAry) !== '[object Array]') throw new Error ("useEffect 函数第二个参数必须是数组")
let prevDeps = preDepsAry[effectIndex]
// 判断是否有变化
let hasChange = prevDeps ? depsAry.every((depsItem,index) => depsItem === prevDeps[index]) === false : true
if(hasChange){
callback()
}
//同步依赖值
preDepsAry[effectIndex] = depsAry
effectIndex++
}
}
function App() {
const [count,setCount] = useState(0)
const [name,setName] = useState('张三')
useEffect (() => {
console.log(22);
},[count])
useEffect (() => {
console.log(44);
},[name])
return (
<div className="App">
{count}
{name}
<button onClick = {( ) => {setCount(count + 1)}}>setCount</button>
<button onClick = { ( ) => {setName("李四")}}>setName</button>
{/* <Colors>
<ShowArea></ShowArea>
<Buttons></Buttons>
</Colors> */}
</div>
);
}
export default App;