避免重复定义回调函数


useCallback

在 React 函数组件中,每一次 UI 的变化,都是通过重新执行整个函数来完成的。

我们知道,当事件处理函数定义在函数组件内部时,在多次渲染之间,是无法重用事件处理函数的,而是每次都需要创建一个新的事件处理函数。

export default function Count(){
    
    
	const [count,setCount] = useState(0)
	const handleClick = () => {
    
    
		console.log('click');
		setCount(count+1)
	}

	return <>
		<button onClick={
    
    handleClick}>{
    
    count}</button>
	</>
}

在上面的例子中,每次组件状态发生变化的时候,函数组件实际上都会重新执行一遍。在每次执行的时候,实际上都会创建一个新的事件处理函数。这个事件处理函数中,包含了 count 这个变量的闭包,以确保每次能够得到正确的结果。

然而这也带来了另一种可能,每次创建新的事件处理函数的方式会让接收事件处理函数的组件重新渲染。

如上面例子的button组件,接收的onClick每次都是新的,都会造成React认为该button组件需要重新渲染。

此时,我们使用useCallback改变这种局面。

//useCallBack使用方式:
useCallback(fn, deps)

useCallBack第一个参数fn是定义的回调函数,deps 是依赖的变量数组。只有当依赖的数组项发生更改才会重新创建回调函数,而我们往往可以借助该hook实现性能优化。

export default function Count(){
    
    
	const [count,setCount] = useState(0)
	const handleClick = useCallback(() => {
    
    
		console.log('click');
		setCount(count+1)
	},[count])

	return <>
		<button onClick={
    
    handleClick}>{
    
    count}</button>
	</>
}

上面的例子通过useCallback实现了只有当依赖项count发生变化时才重新创建回调函数,同时,接收该回调函数作为属性的组件,也不会频繁地需要重新渲染。

useMemo

useMemo与useCallback有相似之处。前者是缓存一个函数,而后者是缓存计算结果。

//useMemo使用方式:
useMemo(fn, deps);

这里可以类比vue的computed,fn需要return一个返回结果。如果某个数据是通过其它数据计算得到的,那么只有当用到的数据,也就是依赖的数据发生变化的时候,才应该需要重新计算。这便是useMemo 的一大好处:避免重复计算。

通过对比useCallback和useMemo,我们不难发现useCallback 的功能其实是可以用 useMemo 来实现的。

const handle = useMemo(() => {
    
    
	// 返回一个函数作为缓存结果
	return () => {
    
    
		// 在这里进行事件处理
	}
}, [dep1, dep2]);

useCallback和useMemo本质上实现目的是相同的,通过建立了一个绑定某个结果到依赖数据的关系。只有当依赖变了,这个结果才需要被重新得到。

useRef

useCallback和useMemo可以缓存结果,然后当我们想在不同的状态变化中共享同一份数据时,就可以借用useRef了。

//useRef的使用方式:
const refValue = useRef(initialValue);

我们可以把 useRef 看作是在函数组件之外创建的一个容器空间。在这个容器上,我们可以通过唯一的 current 属设置一个值,从而在函数组件的多次渲染之间共享这个值。

useRef适用的场景诸如向window存储数据,计时器,保存DOM结构等。

useRef与useState的区别,二者都可以创建数据状态,前者 保存的数据一般是和 UI 的渲染无关的,而后者主要与UI相关,因此当 ref 的值发生变化时,是不会触发组件的重新渲染。

useContext

useContext用于全局状态管理,主要用来管理当前的context,React.createContext API能够让所有在某个组件开始的组件树上创建一个 Context。

//useContext的使用方式:
const value = useContext(MyContext);

这里以官方示例:

React.createContext在某组件创建上下文,通过生产者-消费者模式,在需要使用上下文数据的后代组件里,调用useContext创建变量去接收上下文。

const themes = {
    
    
  light: {
    
    
    foreground: "#000000",
    background: "#eeeeee"
  },
  dark: {
    
    
    foreground: "#ffffff",
    background: "#222222"
  }
};

const ThemeContext = React.createContext(themes.light);

function App() {
    
    
  return (
    <ThemeContext.Provider value={
    
    themes.dark}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar(props) {
    
    
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
    
    
  const theme = useContext(ThemeContext);
  return (
    <button style={
    
    {
    
     background: theme.background, color: theme.foreground }}>
      I am styled by theme context!
    </button>
  );
}

useContext实现了跨组件通信,本质上就是一个全局数据,而与全局变量不同的是,context机制是为了能够进行数据的绑定,当数据变化时能够驱动UI视图的更新。

小结

useCallback、useMemo 和 useEffect的依赖机制都是浅比较。而在回调的定义中,最好的实践是使用useCallback。

useRef区别于useState主要是用于共享数据,作用于与UI无关的事情。

useContext可以在消费者里使用当前上下文数据,而context机制区别于全局变量在于可以实现数据变更更新视图。

猜你喜欢

转载自blog.csdn.net/Mr_RedStar/article/details/124649719