简单理解
- 由于class组件难以理解,代码非常冗余。hook使你在非class的情况下可以使用更多的React特性,拥抱函数。
useState
代码示例
import { useState } from 'react';
export default function Demo() {
let [myNum, setMyNum] = useState(0)
function upMyNum() {
setMyNum((val) => {
let num = val + 1
return num
})
}
let [myObj, setMyObj] = useState({
name: 'hahahaah',
age: 18
})
function upMyObj() {
setMyObj(({ name, age }) => {
console.log(name, age)
return {
name: 'xixiixix',
age: 30
}
})
}
return (
<div>
<div>{myNum}</div>
<button onClick={upMyNum}>加1</button>
<div>名字{myObj.name};年龄{myObj.age}</div>
<button onClick={upMyObj}>更改对象数据</button>
</div>
)
}
- 调用useState中更新数据方式时,是直接做替换
- 在一个函数中useState,可定义多组数据
useEffect
- 类似于class中 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。
代码示例
export default function Demo() {
let [myNum, setMyNum] = useState(0)
function upMyNum() {
setMyNum((val) => {
let num = val + 1
return num
})
}
useEffect(()=>{
console.log('useEffect1')
})
useEffect(() => {
console.log('useEffect2')
},[])
useEffect(() => {
console.log('useEffect3')
}, [myNum])
useEffect(() => {
console.log('useEffect4')
return ()=>{
console.log('清除useEffect4')
}
})
return (
<div>
<div>{myNum}</div>
<button onClick={upMyNum}>加1</button>
</div>
)
}
- 默认情况下,它在第一次渲染之后和每次更新之后都会执行useEffect。
- 在一个函数中useEffect,可使用多次
useContext Provider Consumer
- Context的作用就是对它所包含的组件树提供全局共享数据的一种技术。
代码示例
import { useState, createContext, useContext, } from 'react';
interface Myfrom {
n: number,
setN: Function
}
const MyContext = createContext({} as Myfrom);
export default function Demo() {
const [n, setN] = useState(0)
return (
<div>
<MyContext.Provider value={{ n, setN }}>
<div>这是一级</div>
<button onClick={() => setN(n + 1)}>一级加</button>
<Demo2></Demo2>
</MyContext.Provider>
</div>
)
}
function Demo2() {
return (
<MyContext.Consumer >
{
({ n, setN }) => {
return (
<div>
<div>这是二级:n:{n}</div>
<button onClick={() => setN(n + 1)}>二级加</button>
<Demo3></Demo3>
</div>
)
}
}
</MyContext.Consumer>
)
}
function Demo3() {
const { n, setN } = useContext(MyContext)
return (
<div>
这是三级:n:{n}
<button onClick={() => setN(n + 1)}>三级加</button>
</div>
)
}
- 一般Provider和Consumer,都是成对出现
- useContext的作用与Consumer相同,只是写法上的区别
- useContext和redux的作用是不同的,虽然功能上有类似。前者解决组件嵌套传值,后者解决应用中统一管理状态
useMemo
- 缓存变量
- 所依赖的值改变时,才会重新调用回调函数,并返回计算后的值。
- 避免在每次渲染时都进行高开销的计算
代码示例
const App = function () {
let [num, setNum] = useState(0)
let newNum = useMemo(() => {
return num * 2
}, [num == 3])
return (
<div>
<div>num:{num}</div>
<div>newNum:{newNum}</div>
<button onClick={() => setNum(num + 1)}>加</button>
</div>
)
}
- 以上代码,newNum会重新计算两次。第一次num === 3由flase变为ture,第二次num === 3由ture变为false
- 如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值。
- 不要在useMemo内部执行与渲染无关的操作
memo
- 与React.PureComponent类似,memo用于函数式组件
- 接受2个参数,第一个参数为纯函数的组件,第二个参数用于对比props控制是否刷新,与shouldComponentUpdate()功能类似。
代码示例
import React from "react";
function Child({seconds}){
console.log('I am rendering');
return (
<div>I am update every {seconds} seconds</div>
)
}
function areEqual(prevProps, nextProps) {
if(prevProps.seconds===nextProps.seconds){
return true
}else {
return false
}
}
export default React.memo(Child,areEqual)
- 默认情况memo是通过浅比较,来确认子组件是否渲染
useCallback
- 缓存函数
- 与useMemo基本类似,只是useMemo返回的是计算结果。useCallback返回的是函数
- useCallback(fn, deps) 相当于 useMemo(() => fn, deps)
使用memo;useCallback;useMemo优化子组件渲染
- 正常情况每一次setData,都会重新渲染当前组件和子组件
- 默认情况memo是通过浅比较,来确认子组件是否渲染
- 当父组件传入子组件的参数过于复杂,或者传入方法时(方法都为引用类型)memo无法识别
代码示例
import { useState, useMemo, useCallback, memo } from 'react';
const Child = memo(function (prop) {
console.log('Child渲染')
let { newNum, childEvent } = prop
return (
<div>
<div>newNum:{newNum}</div>
<div onClick={childEvent}>点击</div>
</div>
)
})
const App = function () {
console.log('App渲染')
let [num, setNum] = useState(0)
let [txt, setTxt] = useState('')
let newNum = useMemo(() => {
return num * 2
}, [num == 3])
let childEvent = useCallback(() => {
console.log('子组件触发了我')
setTxt('子组件触发了我')
}, [num == 6])
return (
<div>
<div>num:{num}</div>
<div>newNum:{newNum}</div>
<div>txt:{txt}</div>
<button onClick={() => setNum(num + 1)}>加</button>
<div>-------------以下是子组件---------------</div>
<Child newNum={newNum} childEvent={childEvent}></Child>
</div>
)
}
export default App
- 只要调用setData(无论父调用还是子调用),App组件都会重新渲染
- 上面代码中,Child只会渲染5次,第一次初次加载,在num == 3和num == 6的值变化时,分别有两次。
待续。。。。。。。