- useMemo
React使用Object.is来进行变量的比较(不论是原始类型还是引用类型 的值),它的运算结果和===类似。 这个会导致不必要的re-render,即使变量未发生变化。
所以,有了memoization。
插一句,React.memo使用起来和PureComponent差不多。
useMemo是另一回事:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
依赖列表里面的项若没有发生变化,那么,即便所在的组件re-render,computeExpensiveValue也不会重新执行,只会返回上一次计算的结果。computeExpensiveValue重新执行的条件,就是dependencies发生了变化。这是useMemo最重要的用途。特别是要进行expensive运算的时候,是optimal的选择。
const List = useMemo(
() =>
listOfItems.map(item => ({
...item,
itemProp1: expensiveFunction(props.first),
itemProp2: anotherPriceyFunction(props.second)
})),
[listOfItems]
)
上面整个例子中,只要listOfItems没有发生变化,List就不会重新生成。
function BabyGator({fish, insects}) { // because what else do baby gators eat?
const dinner = {fish, insects};
useEffect(() => {
eatFunction(dinner);
}, [fish, insects])
}
function MamaGator() {
return <BabyGator fish='small edible fish' insects={15}>
}
上面这例子works perfectly well,因为 useEffect的dependencies是两个原始类型的值。 但是, 也仅仅当依赖项是primitive的值时,才达到了不去进行不必要执行eatFunction的目的,这是关键。
所以,即便组件用了React.memo,但组件内部使用的hooks依赖项是引用类型的值,再不用使用上述缓存hooks的方法的情况下,即便引用类型未发生改变,仍然会重新执行该hooks里面的代码。
why?因为在上面讲过,React使用Object.is进行比较,对于引用类型的值来说,比如[] === [] ,肯定是false。所以,如果在dependencies里面添加的依赖是引用类型,则每次都会执行。所以,这就是useMemo和useCallback的用武之地了,它们就是为了在这种场景下发挥作用而被创造出来的,去缓存引用类型的值。
useCallback缓存函数,useMemo缓存值,useRef也是缓存。
需要注意的是,没必要一开始就使用useMemo,使用不当反而会对性能造成耗费。
- useContext
为了防止prop drilling而创造。相对于一层层向下传递数据,它是“退一步”把数据挂在React应用之上,可以跨组件共享数据状态,在某些场景下颇为适用,比如UI主题、语言,鉴权等。 在useContext出来之前,如果要在组件内部引用全局context的数据,需要一些繁琐步骤,此处省略100字... 在没有hooks之前,函数组件通常只能接收props,渲染UI,并不能够承担更多任务。但With Hooks we can manage everything we had used class-based components for.
//定义全局context
const colors = {
blue: "#03619c",
yellow: "#8c8f03",
red: "#9c0312"
};
export const ColorContext = React.createContext(colors.blue);
引入:
import { ColorContext } from "./ColorContext";
function App() {
return (
<ColorContext.Provider value={colors}>
<Home />
</ColorContext.Provider>
);
}
在组件内部使用
import React, { useContext } from "react";
const MyComponent = () => {
const colors = useContext(ColorContext);
return <div style={{ backgroundColor: colors.blue }}>...</div>;
};
- useReducer
useReducer接收三个参数:reducer, initialState, lazyInitFunc(可选参数) 第三个可选参数是个函数,它接收initialState作为参数。这个方法提供了更改 initialState的途径,比如在不同情况/场景/条件下动态计算一个初始值,该计算后的值将覆盖initialState。
const people = [
{name: 'Jay', alive: true},
{name: 'Kailee', alive: true},
{name: 'John', alive: true},
{name: 'Mia', alive: true}
]
const reducer = (people, action) => {
if(action.type == 'chomp') {
return people.map(person => {
if(person.name == action.payload) {
person.alive = false;
}
return person;
})
}
if(action.type == 'revive') {
return people.map(person => {
if(person.name == action.payload) {
person.alive = true;
}
return person;
})
}
}
const deadPeople = () => ([
{ name: 'Jay', alive: false },
{ name: 'Kailee', alive: false },
{ name: 'John', alive: false },
{ name: 'Mia', alive: false }
])
function devour(name) {
dispatch({ type: 'chomp', payload: name });
}
function spitOut(name) {
dispatch({ type: 'revive', payload: name });
}
function App() {
const [state, dispatch] = useReducer(reducer, people, deadPeople);
return (
<div>
{state.map((person, idx) => (
<div key={idx} style={{display: 'flex', width: '50%', justifyContent: 'space-around'}}>
<div>{person.name}</div>
{person.alive ?
<div> ✨✨ ALIVE! ✨✨ <button onClick={() => devour(person.name)}> 🐊 DEVOUR 🐊 </button> </div> :
<div> ☠ ☠ DEAD ☠ ☠ <button onClick={() => spitOut(person.name)}> 🥵 SPIT OUT 🥵 </button> </div>}
</div>
))}
</div>
)
}
提一嘴,useState其实就是useReducer的语法糖.
参考来源: alligator.io/react/useme… alligator.io/react/useco… alligator.io/react/usere…