VUE开发者快速转型React,这是我的React Hooks 总结

我曾经阅读过官方文档,文档中出现过很多新的概念名词,这些概念对于新手或者从其他方向转型过来的朋友非常难理解,这是我的React Hooks 大白话总结,希望帮到大家

useCallback

在组件内定义一个函数声明,这个函数在每次创建组件的时候,是否重新创建取决于两个因素:

1、从未声明创建过函数

2、依赖项参数是值是否发生变化

这里两个条件都会立即触发重新创建函数,如果没有依赖项,则每次返回和之前一样的函数。

变通的理解:你可以认为useCallback包裹的函数,是仅针对当前组件内的函数缓存,可以通过key(依赖项的值,依赖项依然还是传入参数,请不要被这个比喻而误导)的变化引发缓存的刷新 ,无论组件被循环创建多少次,或者组件被销毁重新创建,该函数不会重新创建,会从历史缓存里直接调用,只有依赖项的值发生变化,才会重新创建。

这个hook 解决了内存中函数在组件重绘多次重复创建的问题,下面这个例子由于 useCallback 依赖 productIdreferrer 自上次渲染后始终没有发生改变,因此 handleSubmit 也没有改变。由于 handleSubmit 没有发生改变,ShippingForm 就跳过了重新渲染。

javascript 复制代码
import { useCallback } from 'react';
import ShippingForm from './ShippingForm.js';

export default function ProductPage({ productId, referrer, theme }) {
  const handleSubmit = useCallback((orderDetails) => {
    post('/product/' + productId + '/buy', {
      referrer,
      orderDetails,
    });
  }, [productId, referrer]);

  return (
    <div className={theme}>
      <ShippingForm onSubmit={handleSubmit} />
    </div>
  );
}

function post(url, data) {
  // 想象这发送了一个请求
  console.log('POST /' + url);
  console.log(data);
}

useContext

这是一个面相父子祖孙多代组件树形结构的数据同步hook,首先需要通过

javascript 复制代码
import { createContext, useContext } from 'react';
export const myContext = createContext(null);//导出很重要

来创建一个上下文对象(官方文档并没有在一开始就提起),当然作者把他叫做context我认为有歧义,叫做useContextStore更合适,就像useCallback 叫做useFunctionCache不是更容易理解,

此时在代码中可以用

javascript 复制代码
const [testValue,setTestValue]=useState("我是一只快乐的小鸟")
export default function MyApp() {
//provider 顾名思义为服务提供者,他是myContext提供了的上下文存储的服务组件,
//可以直接接收一个value,这个值就是我们要共享的值
  return (<myContext.provider value={testValue}>
	<child1/>
	<child2/>
	<child3/>
	<child4/>
</myContext.provider>)
}

此时,myContext.provider包裹下的所有组件,如child1、child2、child3、child4 ,同时包括child{$n}的后代组件,都可以直接通过useContext 来获取myContext 绑定的值,这个值,可以通过

setTestValue("世界更美好")的方式直接进行修改,后代组件可以通过useContext的方式获取到共享到myContext的值,如下:

ini 复制代码
const myContextValue = useContext(myContext);

useDebugValue

useDebugValue 用于在开发过程中提供更好的调试信息。它通常在自定义钩子函数内部使用,用于暴露一些有用的值,以便在 React 开发者工具中查看。

useDebugValue 接受两个参数:valueformatvalue 是要暴露的值,而 format 是一个可选的格式化函数,用于将值呈现为更可读的形式。

useDebugValue 的主要目的是让开发者能够在 React 开发者工具中查看有关自定义钩子函数内部状态的信息。例如,当你在自定义钩子函数中使用了某个状态变量,并且你希望在 React 开发者工具中能够看到该变量的值,你可以使用 useDebugValue 来实现这一点。

以下是一个简单的示例,展示了 useDebugValue 的用法:

javascript 复制代码
import { useDebugValue, useState } from 'react';

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

  // 使用 useDebugValue 暴露 count 的值
  useDebugValue(count);

  const increment = () => {
    setCount(count + 1);
  };

  return { count, increment };
}

function MyComponent() {
  const { count, increment } = useCustomHook();

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

在上面的示例中,useCustomHook 是一个自定义钩子函数,它使用了 useDebugValue 来暴露 count 的值。这样,在 React 开发者工具中,你就可以看到 count 的当前值,并且能够更好地调试和理解自定义钩子函数的状态。

useDeferredValue

这是一个延迟更新值得hook,官方文档描述的非常复杂且难以理解,实际上其功能非常实用且简单

实际上,当我们的程序对被useDeferredValue包裹起来的值进行连续修改时,当后台修改触发渲染,当后台未渲染完成,发生了第二次修改,则上一次渲染会被直接中断被抛弃,直到最后一次完整触发渲染,界面上的内容会被更新,这里往往需要配合<Suspense fallback={

Loading...

}>来使用,Suspense 可以在后台进程修改的时候作为占位符提示来使用。

官方明确指出,他与防抖节流是不同的,防抖节流往往需要配合时间间隔的原理来实现,useDeferredValue不同之处在于,其与react深度结合,其渲染效率取决于用户机器的性能,性能越高,延迟几乎不会被看出,每一次修改值可能都会在界面上体现,性能越低的电脑,看到的延迟效果越强烈。

useEffect

在 React 中,"副作用" 是指在组件函数内部执行的与组件渲染结果无关的操作。如:数据订阅和取消订阅、DOM操作、发送网络请求、与第三方库集成、计时器操作等与React无关的操作,在 useEffect 内部,你可以执行副作用操作,并在必要时进行清理,以确保在组件生命周期内正确管理副作用。

官方文档里对两个参数的描述是setup函数和依赖项列表,setup函数我们可以理解为回调函数,实际上就是最终会被调用的函数,依赖项首先可以理解为传入参数,其次可以理解为可影响setup函数再次调用的触发条件,当依赖项值发生变化的时候,setup函数会被重新调用,所以setup函数内的return 是需要返回一个销毁函数的,避免旧的对象未被清理,调用时机可以这么理解:

  1. 将组件挂载到页面时,将运行 setup 代码。

  2. 重新渲染 依赖项 变更的组件后:

    • 首先,使用旧的 props 和 state 运行 cleanup 代码。//cleanup 会执行return的函数内容,进行销毁操作。
    • 然后,使用新的 props 和 state 运行 setup 代码。

    如以下代码

    javascript 复制代码
    import { useEffect, useRef } from 'react';
    
    export default function ModalDialog({ isOpen, children }) {
      const ref = useRef();
    //isOpen作为依赖项,当值发生改变时,触发一个setup函数
      useEffect(() => {
        if (!isOpen) {
          return;
        }
        const dialog = ref.current;
        dialog.showModal();
        return () => {
         //这里是给提供的清理操作,也可以理解为销毁函数
          dialog.close();
        };
      }, [isOpen]);
    
      return <dialog ref={ref}>{children}</dialog>;
    }

    在这个例子中,外部系统是 animation.js 中的动画库。它提供了一个名为 FadeInAnimation 的 JavaScript 类,该类接受一个 DOM 节点作为参数,并暴露 start()stop() 方法来控制动画。此组件 使用 ref 访问底层 DOM 节点。Effect 从 ref 中读取 DOM 节点,并在组件出现时自动开启该节点的动画,所以依赖项并不是必须的。

    javascript 复制代码
    //这是一个动画控制的示例
    import { useState, useEffect, useRef } from 'react';
    import { FadeInAnimation } from './animation.js';
    
    function Welcome() {
      const ref = useRef(null);
    
      useEffect(() => {
        const animation = new FadeInAnimation(ref.current);
        animation.start(1000);
        return () => {
        //这里是给提供的清理操作,也可以理解为销毁函数
          animation.stop();
        };
      }, []);
    
      return (
        <h1
          ref={ref}
          style={{
            opacity: 0,
            color: 'white',
            padding: 50,
            textAlign: 'center',
            fontSize: 50,
            backgroundImage: 'radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%)'
          }}
        >
          Welcome
        </h1>
      );
    }
    
    export default function App() {
      const [show, setShow] = useState(false);
      return (
        <>
          <button onClick={() => setShow(!show)}>
            {show ? 'Remove' : 'Show'}
          </button>
          <hr />
          {show && <Welcome />}
        </>
      );
    }

    useId

    useId很简单,只是一个为当前组件生成一个唯一id而存在,请勿将他用来生成数据列表的id,数据列表的id应该由数据服务方来生成。

    此外useId还可以配置全局前缀,代码如下:

    javascript 复制代码
    const app = createRoot(document.getElementById('app'), {
      identifierPrefix: 'my-first-app-' //这里来修改前缀
    });
    app.render(<App />);

    useImperativeHandle

    这是一个不常用的hook,作用是让子组件将自身的ref自定义函数操作暴露给父组件。

    必须使用forwardRef,这样才会被父组件访问到

    javascript 复制代码
    import { forwardRef } from 'react';
    
    const MyInput = forwardRef(function MyInput(props, ref) {
      return <input {...props} ref={ref} />;
    });

    不要滥用 ref

    你应当仅在你没法通过 prop 来表达 命令式 行为的时候才使用 ref:例如,滚动到指定节点、聚焦某个节点、触发一次动画,以及选择文本等等。

    如果可以通过 prop 实现,那就不应该使用 ref

    例如,你不应该从一个 Model 组件暴露出 {open, close} 这样的命令式句柄,最好是像 <Modal isOpen={isOpen} /> 这样,将 isOpen 作为一个 prop。副作用 可以帮你通过 prop 来暴露一些命令式的行为。

    完整的例子

    父组件 ParentComponent 使用 useRef 创建一个 childRef,并将其传递给 ChildComponent 的 ref 属性。当点击父组件中的按钮时,调用 childRef.current.focus() 来触发子组件中的 focus 方法,从而将焦点设置到子组件的输入框上。

    javascript 复制代码
    import { useRef, useImperativeHandle, forwardRef } from 'react';
    
    // 子组件
    const ChildComponent = forwardRef((props, ref) => {
      const inputRef = useRef(null);
    
      // 定义暴露给父组件的方法
      useImperativeHandle(ref, () => ({
        focus: () => {
          inputRef.current.focus();
        }
      }));
    
      return <input type="text" ref={inputRef} />;
    });
    
    // 父组件
    const ParentComponent = () => {
      const childRef = useRef(null);
    
      const handleClick = () => {
        childRef.current.focus();
      };
    
      return (
        <div>
          <ChildComponent ref={childRef} />
          <button onClick={handleClick}>Focus Child</button>
        </div>
      );
    };

    useMemo

    当依赖项不变时,被useMemo包裹的函数不会重复执行,会从缓存中直接响应,减少了不必要的重复计算,提高了计算效率。

    示例

    当需要对一个数字进行平方计算时,可以使用 useMemo 对计算结果进行记忆化处理。这样,在依赖项变化时才重新执行计算,避免不必要的重复计算。

    typescript 复制代码
    import { useMemo, useState } from 'react';
    
    function SquareCalculator() {
    		const [number, setNumber] = useState(0);
    		
    		const squaredNumber = useMemo(() => {
    				return number ** 2;
    		}, [number]);
    
    		return (
    				<div>
    						<input
    						type="number"
    						value={number}
    						onChange={(e) => setNumber(Number(e.target.value))}
    						/>
    						<p>Square of {number} is: {squaredNumber}</p>
    				</div>
    				);
    	}

    useState

    声明一个状态变量,会返回一个由两个值组成的数组的值,分别为:

    1. 当前的 state。在首次渲染时,它将与你传递的 initialState 相匹配。
    2. set 函数,它可以让你将 state 更新为不同的值并触发重新渲染。

    可以使用如下方式接收和调用:

    javascript 复制代码
    import { useState } from 'react';
    
    export default function Counter() {
      const [count, setCount] = useState(0);
    
      function handleClick() {
        setCount(count + 1);
      }
    
      return (
        <button onClick={handleClick}>
          You pressed me {count} times
        </button>
      );
    }

    useReducer

    useReduceruseState 非常相似,但是它可以让你把状态更新逻辑从事件处理函数中移动到组件外部,作为全局store来使用,并且可以同时处理多个字段。

    示例

    javascript 复制代码
    import { useReducer } from 'react';
    
    function reducer(state, action) {
      if (action.type === 'incremented_age') {
        return {
          age: state.age + 1
        };
      }
      throw Error('Unknown action.');
    }
    
    export default function Counter() {
      const [state, dispatch] = useReducer(reducer, { age: 42 });
    
      return (
        <>
          <button onClick={() => {
    //值得注意的是 reducer是只读函数,需要通过 dispatch 来修改指定的值,传入的参数为action
            dispatch({ type: 'incremented_age' })
          }}>
            Increment age
          </button>
          <p>Hello! You are {state.age}.</p>
        </>
      );
    }

    useRef

    useRef 通常是用来操作DOM节点的,当然官方自身定义是为了实现一个与渲染无关的值的声明,需要通过ref.current 的方式来取值,有点被代理的意思,但我们大多数情况下还是用来才做DOM的引用,毕竟用来声明一个值是有点多此一举了。

    示例

    javascript 复制代码
    import { useRef } from 'react';
    
    export default function Form() {
      const inputRef = useRef(null);
    
      function handleClick() {
        inputRef.current.focus();
      }
    
      return (
        <>
          <input ref={inputRef} />
          <button onClick={handleClick}>
            Focus the input
          </button>
        </>
      );
    }

    useTransition

    使用 useTransition 可以帮助我们在异步操作期间提供更好的用户体验,通过平滑的过渡效果来避免应用的卡顿和阻塞。

    useTransition 返回一个数组,其中包含两个元素:startTransitionisPending

    • startTransition 是一个函数,用于触发异步操作的开始,并在操作完成后更新组件以及isPending的值,目前据我了解,对promise then语法支持良好。
    • isPending 是一个布尔值,表示异步操作是否仍在进行中。

    示例

    通过 useTransition,我们获取到 startTransitionisPending。当用户点击按钮时,调用 startTransition 并传入一个回调函数,该函数用于执行异步操作。在回调函数中,我们可以执行需要的异步操作,例如数据加载。

    按钮的 disabled 属性根据 isPending 的值来决定,以防止用户在异步操作进行中重复点击。

    Suspense 组件用于包裹异步加载的组件,并提供一个 fallback 属性,用于在异步操作进行中显示一个加载中的提示。

    javascript 复制代码
    import { useTransition, Suspense } from 'react';
    
    const MyComponent = () => {
      const [startTransition, isPending] = useTransition();
      const fetchData = () => {
        startTransition(() => {
          // 异步操作开始
          // 可以在这里进行数据加载等异步操作
        });
      };
    
      return (
        <div>
          <button onClick={fetchData} disabled={isPending}>
            {isPending ? 'Loading...' : 'Fetch Data'}
          </button>
          <Suspense fallback={<div>Loading...</div>}>
            {/* 渲染异步加载的组件 */}
          </Suspense>
        </div>
      );
    };

    文章中如果有错误,请及时指出,感谢点赞

相关推荐
学不会•31 分钟前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
活宝小娜3 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点3 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow3 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o3 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app
开心工作室_kaic4 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
刚刚好ā4 小时前
js作用域超全介绍--全局作用域、局部作用、块级作用域
前端·javascript·vue.js·vue
沉默璇年5 小时前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder5 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
2401_882727576 小时前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架