初识React Hooks-下篇

本文深入探讨React Hooks,重点解析useReducer的使用场景,对比useState,以及讲解useCallback和useMemo如何优化性能。同时,详细阐述useRef的特性,包括如何获取最新值,最后提到了useImperativeHandle、useLayoutEffect和useDebugValue三个额外Hook的简介。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇已经基本介绍了React Hooks的基础,以及基础Hooks的使用,但还不够详细,这篇将详细的介绍基础Hooks和额外Hooks的使用。

– useReducer

1.是什么?

它是useState的替代方案,它接收一个reducer和初始值,并返回当前state及其成对的dispatch. 其中reducer是一个接收state和action并返回当前state的纯函数。(不了解action、reducer的,可以先了解一下redux)

const [state, dispatch] = useReducer(reducer, initialArg, init);

例如,我们用官网中的一个例子来说明一下:

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

上述代码中,在组件Counter中使用了useReducer。

  • useReducer返回的dispatch方法分别用在了两个按钮上,用来执行对state的不同操作。
  • 而后reducer根据参数action的类型不同,分别执行增加和减小操作,并返回操作后的state,以实现对state的更新。

2. 为什么要用useReducer?

可能很多人都习惯用useState,但是当某一个state是数组或者对象、或者一个操作能引起state多个属性发生变化,就可以使用useReducer来简化操作。

  • 我们可以使用useReducer来处理请求的返回情况。
const reducer = (state, action) => {
	switch(action.type){
		case 'success' : alert('success');
		case 'fail': alert('fail');
	}
}
function Reducer(){
	const [state, dispatch] = useReducer(reducer, initialState);
	axios.get(
	   url,
	).then(res => {
	   if (res.status === 200){
	      if (res.data.code === 100000) {
	         dispatch(type: 'success')
	      }else{
	         dispatch(type: 'fail')
	      }
		}
	})
}

3. useReducer和useState的渊源

useState是由useReducer来实现的,源码如下。当我们调用useState的时候,实际上调用的是useReducer。

function basicStateReducer(state, action) {
  return typeof action === 'function' ? action(state) : action;
}
function useState(initialState) {
  {
    currentHookNameInDev = 'useState';
  }
  //返回useReducer
  return useReducer(basicStateReducer, // useReducer has a special case to support lazy useState initializers
  initialState);
}

– useCallback && useMemo

1. 是什么

这两个hook和useEffect的依赖参数一样,并且都会返回缓存的值,useCallback返回缓存的函数,useMemo返回缓存的变量。

const memoizedCallback = useCallback(() => {
    doSomething(a, b);
},[a, b],);

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

2. 为什么要用?

我也在想,既然这两个hook和useEffect的写法是一样的,那么在什么情况下会用到呢?
首先,useEffect能有类组件中生命周期的作用。它都是自动执行的。而useCallback和useMemo会返回一个memoized 回调函数,它需要手动调用才去执行。例如一个点击事件:

import React, { useEffect, useCallback, useMemo, useState} from "react";

function CallbackCom() {
    const [count, setCount] = useState(0);

    handleClick = () => {
        console.log('click...', count)
    }
	//使用useCallback的写法
	const memoizedHandleClick = useCallback(() => 			
		console.log(`Click happened with dependency: ${count}`), [count],); 

    return (
        <div>
            <p>{count}</p>
            <button onClick={handleClick}>点我</button>
        </div>
    )
}
export default CallbackCom;

每次组件CallbackCom重新渲染的时候,传给 button的handleClick都是一个新建的函数,所以useCallback可以在依赖项不变的时候,返回相同的引用,从而减少不必要的函数创建。同理,在使用useMemo时,可以减少不必要的一些值得计算。

– useRef

1. 是什么

useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。

const refContainer = useRef(initialValue);

2. 为什么要用useRef

在用法上,useRef和createRef几乎一样,官网上的例子如下.同样用createRef时,只需把useRef进行替换即可。

function TextInputWithFocusButton() {
  const inputEl = useRef(null);// const inputEl = createRef();
  const onButtonClick = () => {
    // `current` 指向已挂载到 DOM 上的文本输入元素
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

既然写法一样,那到底有什么不一样的地方让useRef区分于createRef呢?
那就是useRef返回的 ref 对象在组件的整个生命周期内保持不变。它在每次渲染时每次都会返回同一个引用,而createRef每次都会返回不同的引用。

import React, { createRef, useRef } from "react";

const ref_list = [];

const App_useRef = () => {
  const [renderIndex, setRenderIndex] = React.useState(1);
  const refFromUseRef = useRef();
  const refFromCreateRef = createRef();
  console.log('111111');
  console.log('refFromUseRef.current',refFromUseRef.current)//初始化为undefined,以后每次渲染后输出都是1
  console.log('refFromCreateRef.current', refFromCreateRef.current)//初始化为null,以后每次渲染后输出都加1

	//只会执行一次
  if (!refFromUseRef.current) {
    refFromUseRef.current = renderIndex;
  }

	//每次渲染后都会执行
  if (!refFromCreateRef.current) {
    refFromCreateRef.current = renderIndex;
  }

  return (
    <>
      <p>Current render index: {renderIndex}</p>
      <p>
        <b>refFromUseRef</b> value: {refFromUseRef.current}
      </p>
      <p>
        <b>refFromCreateRef</b> value:{refFromCreateRef.current}
      </p>

      <button onClick={() => setRenderIndex(renderIndex+1)}>
        Cause re-render
      </button>
    </>
  );
};

export default App_useRef;

由于useRef会一直返回同一个引用,所以它返回的一直都是最开始的那个renderIndex;而createRef在每次渲染后都返回新的引用,所以它的返回值是每次新的renderIndex。

说到这里,有个疑问,为什么useRef每次的返回值一直都是初始化时的值,那么怎么样才能让它和createRef一样获取到最新的值呢?

3. useRef获取最新的值

const App_useRef = () => {
	const [count,setCount] = React.useState(0);
	const useref = useRef();

	useEffect(()=>{
		console.log('count...', count);
  		useref.current = count;
	})
	return(
		<p>{count}</p>
	    <p>{useref.current}</p>
	    <button onClick={() => setCount(count+1)}>cause re-			render</button>
	)
}

由于useEffect会在每次re-render后执行,所以在useEffect中输出的count是每次更新后的值,此时useref.current也就能表示最新的count的值。

有一点需要说明的是,它表示的值虽然更新了,但是新值并没有能实时的显示到界面上,也就是界面上显示的useref.current永远是上一次的值。

剩余的三个hook: useImperativeHandle、useLayoutEffect、useDebugValue可以参考官方文档进行简单了解。

希望大家能持续关注哦,留一些个人信息方便大家找到我哦。知乎 github
附上个人公众号哦,希望大家前来骚扰(也会经常写一些随笔来记录生活,毕竟人生漫漫)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值