欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > IT业 > WHAT - 通过 react-use 源码学习 React(UI 篇)

WHAT - 通过 react-use 源码学习 React(UI 篇)

2025/6/6 9:43:32 来源:https://blog.csdn.net/weixin_58540586/article/details/141640475  浏览:    关键词:WHAT - 通过 react-use 源码学习 React(UI 篇)

目录

  • 一、官方介绍
    • 1. Sensors
    • 2. UI
    • 3. Animations
    • 4. Side-Effects
    • 5. Lifecycles
    • 6. State
    • 7. Miscellaneous
  • 二、源码学习
    • 示例:n. xx - yy
    • UI - useAudio
      • `createHTMLMediaHook` 函数解析
        • 功能
        • 参数
        • **返回值**
      • **实现细节**
      • **总结**

一、官方介绍

Github 地址

react-use 是一个流行的 React 自定义 Hook 库,提供了一组常用的 Hook,以帮助开发者在 React 应用程序中更方便地处理常见的任务和功能。

官方将 react-use 的 Hook 分成了以下几个主要类别,以便更好地组织和查找常用的功能。每个类别涵盖了不同类型的 Hook,满足各种开发需求。以下是这些类别的详细说明:

1. Sensors

  • 功能: 主要涉及与浏览器或用户交互相关的传感器功能。
  • 示例:
    • useMouse: 获取鼠标位置。
    • useWindowSize: 获取窗口尺寸。
    • useBattery: 监控电池状态。

2. UI

  • 功能: 涉及用户界面相关的功能,如处理样式、显示和隐藏元素等。
  • 示例:
    • useClickAway: 监听点击事件以检测用户点击是否发生在组件外部。
    • useMeasure: 测量元素的大小和位置。
    • useDarkMode: 管理和检测暗模式状态。

3. Animations

  • 功能: 处理动画和过渡效果。
  • 示例:
    • useSpring: 使用 react-spring 处理动画效果。
    • useTransition: 使用 react-spring 处理过渡动画。

4. Side-Effects

  • 功能: 处理副作用相关的 Hook,包括数据获取、异步操作等。
  • 示例:
    • useAsync: 处理异步操作,如数据获取,并提供状态和结果。
    • useFetch: 简化数据获取操作。
    • useAxios: 使用 Axios 进行数据请求的 Hook。

5. Lifecycles

  • 功能: 处理组件生命周期相关的 Hook。
  • 示例:
    • useMount: 在组件挂载时执行的 Hook。
    • useUnmount: 在组件卸载时执行的 Hook。
    • useUpdate: 在组件更新时执行的 Hook。

6. State

  • 功能: 管理组件状态和相关逻辑。
  • 示例:
    • useState: 提供基本状态管理功能。
    • useReducer: 替代 useState 实现更复杂的状态逻辑。
    • useForm: 管理表单状态和验证。
    • useInput: 管理输入字段的状态。

7. Miscellaneous

  • 功能: 各种其他实用功能的 Hook,涵盖一些不容易归类到其他类别的功能。

这种分类方法使得 react-use 的 Hook 更加有组织和易于查找,帮助开发者快速找到需要的功能并有效地集成到他们的应用程序中。

二、源码学习

示例:n. xx - yy

something

使用

源码

解释

UI - useAudio

plays audio and exposes its controls.

使用

import {useAudio} from 'react-use';const Demo = () => {const [audio, state, controls, ref] = useAudio({src: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3',autoPlay: true,});return (<div>{audio}<pre>{JSON.stringify(state, null, 2)}</pre><button onClick={controls.pause}>Pause</button><button onClick={controls.play}>Play</button><br/><button onClick={controls.mute}>Mute</button><button onClick={controls.unmute}>Un-mute</button><br/><button onClick={() => controls.volume(.1)}>Volume: 10%</button><button onClick={() => controls.volume(.5)}>Volume: 50%</button><button onClick={() => controls.volume(1)}>Volume: 100%</button><br/><button onClick={() => controls.seek(state.time - 5)}>-5 sec</button><button onClick={() => controls.seek(state.time + 5)}>+5 sec</button></div>);
};

源码

import createHTMLMediaHook from './factory/createHTMLMediaHook';const useAudio = createHTMLMediaHook<HTMLAudioElement>('audio');
export default useAudio;
//./factory/createHTMLMediaHook
import * as React from 'react';
import { useEffect, useRef } from 'react';
import useSetState from '../useSetState';
import parseTimeRanges from '../misc/parseTimeRanges';export interface HTMLMediaPropsextends React.AudioHTMLAttributes<any>,React.VideoHTMLAttributes<any> {src: string;
}export interface HTMLMediaState {buffered: any[];duration: number;paused: boolean;muted: boolean;time: number;volume: number;playing: boolean;
}export interface HTMLMediaControls {play: () => Promise<void> | void;pause: () => void;mute: () => void;unmute: () => void;volume: (volume: number) => void;seek: (time: number) => void;
}type MediaPropsWithRef<T> = HTMLMediaProps & { ref?: React.MutableRefObject<T | null> };export default function createHTMLMediaHook<T extends HTMLAudioElement | HTMLVideoElement>(tag: 'audio' | 'video'
) {return (elOrProps: HTMLMediaProps | React.ReactElement<HTMLMediaProps>) => {let element: React.ReactElement<MediaPropsWithRef<T>> | undefined;let props: MediaPropsWithRef<T>;if (React.isValidElement(elOrProps)) {element = elOrProps;props = element.props;} else {props = elOrProps;}const [state, setState] = useSetState<HTMLMediaState>({buffered: [],time: 0,duration: 0,paused: true,muted: false,volume: 1,playing: false,});const ref = useRef<T | null>(null);const wrapEvent = (userEvent, proxyEvent?) => {return (event) => {try {proxyEvent && proxyEvent(event);} finally {userEvent && userEvent(event);}};};const onPlay = () => setState({ paused: false });const onPlaying = () => setState({ playing: true });const onWaiting = () => setState({ playing: false });const onPause = () => setState({ paused: true, playing: false });const onVolumeChange = () => {const el = ref.current;if (!el) {return;}setState({muted: el.muted,volume: el.volume,});};const onDurationChange = () => {const el = ref.current;if (!el) {return;}const { duration, buffered } = el;setState({duration,buffered: parseTimeRanges(buffered),});};const onTimeUpdate = () => {const el = ref.current;if (!el) {return;}setState({ time: el.currentTime });};const onProgress = () => {const el = ref.current;if (!el) {return;}setState({ buffered: parseTimeRanges(el.buffered) });};if (element) {element = React.cloneElement(element, {controls: false,...props,ref,onPlay: wrapEvent(props.onPlay, onPlay),onPlaying: wrapEvent(props.onPlaying, onPlaying),onWaiting: wrapEvent(props.onWaiting, onWaiting),onPause: wrapEvent(props.onPause, onPause),onVolumeChange: wrapEvent(props.onVolumeChange, onVolumeChange),onDurationChange: wrapEvent(props.onDurationChange, onDurationChange),onTimeUpdate: wrapEvent(props.onTimeUpdate, onTimeUpdate),onProgress: wrapEvent(props.onProgress, onProgress),});} else {element = React.createElement(tag, {controls: false,...props,ref,onPlay: wrapEvent(props.onPlay, onPlay),onPlaying: wrapEvent(props.onPlaying, onPlaying),onWaiting: wrapEvent(props.onWaiting, onWaiting),onPause: wrapEvent(props.onPause, onPause),onVolumeChange: wrapEvent(props.onVolumeChange, onVolumeChange),onDurationChange: wrapEvent(props.onDurationChange, onDurationChange),onTimeUpdate: wrapEvent(props.onTimeUpdate, onTimeUpdate),onProgress: wrapEvent(props.onProgress, onProgress),} as any); // TODO: fix this typing.}// Some browsers return `Promise` on `.play()` and may throw errors// if one tries to execute another `.play()` or `.pause()` while that// promise is resolving. So we prevent that with this lock.// See: https://bugs.chromium.org/p/chromium/issues/detail?id=593273let lockPlay: boolean = false;const controls = {play: () => {const el = ref.current;if (!el) {return undefined;}if (!lockPlay) {const promise = el.play();const isPromise = typeof promise === 'object';if (isPromise) {lockPlay = true;const resetLock = () => {lockPlay = false;};promise.then(resetLock, resetLock);}return promise;}return undefined;},pause: () => {const el = ref.current;if (el && !lockPlay) {return el.pause();}},seek: (time: number) => {const el = ref.current;if (!el || state.duration === undefined) {return;}time = Math.min(state.duration, Math.max(0, time));el.currentTime = time;},volume: (volume: number) => {const el = ref.current;if (!el) {return;}volume = Math.min(1, Math.max(0, volume));el.volume = volume;setState({ volume });},mute: () => {const el = ref.current;if (!el) {return;}el.muted = true;},unmute: () => {const el = ref.current;if (!el) {return;}el.muted = false;},};useEffect(() => {const el = ref.current!;if (!el) {if (process.env.NODE_ENV !== 'production') {if (tag === 'audio') {console.error('useAudio() ref to <audio> element is empty at mount. ' +'It seem you have not rendered the audio element, which it ' +'returns as the first argument const [audio] = useAudio(...).');} else if (tag === 'video') {console.error('useVideo() ref to <video> element is empty at mount. ' +'It seem you have not rendered the video element, which it ' +'returns as the first argument const [video] = useVideo(...).');}}return;}setState({volume: el.volume,muted: el.muted,paused: el.paused,});// Start media, if autoPlay requested.if (props.autoPlay && el.paused) {controls.play();}}, [props.src]);return [element, state, controls, ref] as const;};
}

解释

createHTMLMediaHook 是一个高阶函数,用于创建 React 自定义 Hook,简化了对 <audio><video> 元素的控制。它结合了 React 的状态管理和生命周期钩子来提供一个便捷的接口,用于处理 HTML 媒体元素的播放、暂停、音量控制等操作。以下是对 createHTMLMediaHook 函数的详细解析。

createHTMLMediaHook 函数解析

功能

createHTMLMediaHook 主要用于创建处理 HTML 媒体元素(如 <audio><video>)的 Hook。这个 Hook 封装了对媒体元素的常见操作和状态管理,提供了一个统一的接口来操作和控制媒体元素。

参数
  • tag:
    • 类型: 'audio' | 'video'
    • 说明: 指定要创建的 HTML 媒体元素类型,可以是 'audio''video'
返回值
  • 返回一个函数,该函数接受两种可能的参数:
    • elOrProps:
      • 类型: HTMLMediaProps | React.ReactElement<HTMLMediaProps>
      • 说明: 可以是媒体元素的属性对象,也可以是包含媒体属性的 React 元素。
    • 返回值是一个元组 [element, state, controls, ref]
      • element: 渲染的 React 元素(<audio><video>)。
      • state: 当前的媒体状态(HTMLMediaState)。
      • controls: 控制媒体播放的函数(HTMLMediaControls)。
      • ref: 对应媒体元素的 ref

实现细节

  1. 参数处理

    let element: React.ReactElement<MediaPropsWithRef<T>> | undefined;
    let props: MediaPropsWithRef<T>;if (React.isValidElement(elOrProps)) {element = elOrProps;props = element.props;
    } else {props = elOrProps;
    }
    
    • 如果 elOrProps 是一个 React 元素,则提取其属性。
    • 否则,elOrProps 被认为是直接的媒体属性。
  2. 状态和引用

    const [state, setState] = useSetState<HTMLMediaState>({buffered: [],time: 0,duration: 0,paused: true,muted: false,volume: 1,playing: false,
    });
    const ref = useRef<T | null>(null);
    
    • 使用 useSetState 管理媒体状态。有关 useSetState 具体解释可以阅读 WHAT - 通过 react-use 源码学习 React(State 篇)
    • ref 是一个 useRef,用于引用实际的媒体元素。
  3. 事件处理

    const wrapEvent = (userEvent, proxyEvent?) => {return (event) => {try {proxyEvent && proxyEvent(event);} finally {userEvent && userEvent(event);}};
    };
    
    • wrapEvent 用于将用户提供的事件处理函数和内部事件处理函数组合在一起。

    • 内部事件处理函数(如 onPlayonPause 等)更新状态以反映媒体元素的当前状态。

  4. 创建和渲染媒体元素

    if (element) {element = React.cloneElement(element, {controls: false,...props,ref,onPlay: wrapEvent(props.onPlay, onPlay),onPlaying: wrapEvent(props.onPlaying, onPlaying),onWaiting: wrapEvent(props.onWaiting, onWaiting),onPause: wrapEvent(props.onPause, onPause),onVolumeChange: wrapEvent(props.onVolumeChange, onVolumeChange),onDurationChange: wrapEvent(props.onDurationChange, onDurationChange),onTimeUpdate: wrapEvent(props.onTimeUpdate, onTimeUpdate),onProgress: wrapEvent(props.onProgress, onProgress),});
    } else {element = React.createElement(tag, {controls: false,...props,ref,onPlay: wrapEvent(props.onPlay, onPlay),onPlaying: wrapEvent(props.onPlaying, onPlaying),onWaiting: wrapEvent(props.onWaiting, onWaiting),onPause: wrapEvent(props.onPause, onPause),onVolumeChange: wrapEvent(props.onVolumeChange, onVolumeChange),onDurationChange: wrapEvent(props.onDurationChange, onDurationChange),onTimeUpdate: wrapEvent(props.onTimeUpdate, onTimeUpdate),onProgress: wrapEvent(props.onProgress, onProgress),} as any); // TODO: fix this typing.
    }
    
    • 如果提供了元素,则克隆并扩展其属性。
    • 如果没有提供元素,则创建新的媒体元素。
  5. 控制方法

    const controls = {play: () => { /* ... */ },pause: () => { /* ... */ },seek: (time: number) => { /* ... */ },volume: (volume: number) => { /* ... */ },mute: () => { /* ... */ },unmute: () => { /* ... */ },
    };
    
    • controls 对象提供了对媒体元素进行播放、暂停、音量调整等操作的方法。
    • playpause 方法处理 Promise,确保不会重复调用 play 方法。
    • seek 方法调整播放时间。
    • volumemuteunmute 方法调整音量和静音状态。
  6. 副作用

    useEffect(() => {const el = ref.current!;if (!el) {// Handle errorreturn;}setState({volume: el.volume,muted: el.muted,paused: el.paused,});if (props.autoPlay && el.paused) {controls.play();}
    }, [props.src]);
    
    • 在组件挂载时,设置初始状态并根据 props.autoPlay 自动播放媒体。

总结

  • createHTMLMediaHook: 用于创建一个自定义 Hook 来处理 <audio><video> 元素,封装了媒体元素的控制和状态管理。
  • 事件处理: 内部事件处理函数更新 Hook 状态,以反映媒体元素的当前状态。
  • controls: 提供了用于播放、暂停、调整音量等功能的方法。
  • 副作用: 通过 useEffect 确保在媒体源更改时更新状态,并根据 autoPlay 属性自动播放。

这个 Hook 提供了一个简洁的 API 来处理媒体元素的常见操作,使得在 React 组件中操作音视频元素变得更加方便和一致。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词