1- import { useEffect , useState } from 'react' ;
1+ import { useEffect , useRef } from 'react' ;
22import { isTarget } from '@/utils/helpers' ;
33import { useRefState } from '../useRefState/useRefState' ;
4+ import { useRerender } from '../useRerender/useRerender' ;
45/**
56 * @name useMouse
67 * @description - Hook that manages a mouse position
@@ -9,21 +10,24 @@ import { useRefState } from '../useRefState/useRefState';
910 *
1011 * @overload
1112 * @param {HookTarget } [target=window] The target element to manage the mouse position for
12- * @returns {UseMouseReturn } An object with the current mouse position
13+ * @param {(value: UseMouseValue, event: Event) => void } [callback] The callback to invoke on mouse updates
14+ * @returns {UseMouseReturn } An object with mouse value controls
1315 *
1416 * @example
15- * const { x, y, clientX, clientY, elementX, elementY, elementPositionX, elementPositionY } = useMouse(ref);
17+ * const mouse = useMouse(ref);
1618 *
1719 * @overload
1820 * @template Target The target element
19- * @returns {UseMouseReturn & { ref: StateRef<Target> } } An object with the current mouse position and a ref
21+ * @param {(value: UseMouseValue, event: Event) => void } [callback] The callback to invoke on mouse updates
22+ * @returns {UseMouseReturn & { ref: StateRef<Target> } } An object with mouse value controls and a ref
2023 *
2124 * @example
22- * const { ref, x, y, clientX, clientY, elementX, elementY, elementPositionX, elementPositionY } = useMouse();
25+ * const mouse = useMouse<HTMLDivElement> ();
2326 */
2427export const useMouse = ( ...params ) => {
2528 const target = isTarget ( params [ 0 ] ) ? params [ 0 ] : undefined ;
26- const [ value , setValue ] = useState ( {
29+ const callback = target ? params [ 1 ] : params [ 0 ] ;
30+ const snapshotRef = useRef ( {
2731 x : 0 ,
2832 y : 0 ,
2933 elementX : 0 ,
@@ -33,15 +37,32 @@ export const useMouse = (...params) => {
3337 clientX : 0 ,
3438 clientY : 0
3539 } ) ;
40+ const internalCallbackRef = useRef ( callback ) ;
41+ internalCallbackRef . current = callback ;
42+ const watchingRef = useRef ( false ) ;
43+ const rerender = useRerender ( ) ;
3644 const internalRef = useRefState ( ) ;
45+ const updateValue = ( nextValue , event ) => {
46+ snapshotRef . current = nextValue ;
47+ internalCallbackRef . current ?. ( nextValue , event ) ;
48+ if ( watchingRef . current ) rerender ( ) ;
49+ } ;
50+ const watch = ( ) => {
51+ watchingRef . current = true ;
52+ return snapshotRef . current ;
53+ } ;
3754 useEffect ( ( ) => {
3855 const onMouseMove = ( event ) => {
3956 const element = target ? isTarget . getElement ( target ) : internalRef . current ;
4057 const updatedValue = {
4158 x : event . pageX ,
4259 y : event . pageY ,
4360 clientX : event . clientX ,
44- clientY : event . clientY
61+ clientY : event . clientY ,
62+ elementX : event . pageX ,
63+ elementY : event . pageY ,
64+ elementPositionX : 0 ,
65+ elementPositionY : 0
4566 } ;
4667 if ( element ) {
4768 const { left, top } = element . getBoundingClientRect ( ) ;
@@ -53,29 +74,18 @@ export const useMouse = (...params) => {
5374 updatedValue . elementY = elementY ;
5475 updatedValue . elementPositionX = elementPositionX ;
5576 updatedValue . elementPositionY = elementPositionY ;
56- setValue ( ( prevValue ) => ( {
57- ...prevValue ,
58- ...updatedValue
59- } ) ) ;
60- } else {
61- updatedValue . elementX = event . pageX ;
62- updatedValue . elementY = event . pageY ;
63- updatedValue . elementPositionX = 0 ;
64- updatedValue . elementPositionY = 0 ;
65- setValue ( ( prevValue ) => ( {
66- ...prevValue ,
67- ...updatedValue
68- } ) ) ;
6977 }
78+ updateValue ( updatedValue , event ) ;
7079 } ;
71- const onScroll = ( ) => {
72- setValue ( ( prevValue ) => ( {
73- ...prevValue ,
74- x : prevValue . x + window . scrollX - prevValue . elementPositionX ,
75- y : prevValue . y + window . scrollY - prevValue . elementPositionY ,
80+ const onScroll = ( event ) => {
81+ const updatedValue = {
82+ ...snapshotRef . current ,
83+ x : snapshotRef . current . x + window . scrollX - snapshotRef . current . elementPositionX ,
84+ y : snapshotRef . current . y + window . scrollY - snapshotRef . current . elementPositionY ,
7685 elementPositionX : window . scrollX ,
7786 elementPositionY : window . scrollY
78- } ) ) ;
87+ } ;
88+ updateValue ( updatedValue , event ) ;
7989 } ;
8090 document . addEventListener ( 'scroll' , onScroll , { passive : true } ) ;
8191 document . addEventListener ( 'mousemove' , onMouseMove ) ;
@@ -84,9 +94,10 @@ export const useMouse = (...params) => {
8494 document . removeEventListener ( 'mousemove' , onMouseMove ) ;
8595 } ;
8696 } , [ internalRef . state , target && isTarget . getRawElement ( target ) ] ) ;
87- if ( target ) return value ;
97+ if ( target ) return { snapshot : snapshotRef . current , watch } ;
8898 return {
8999 ref : internalRef ,
90- ...value
100+ snapshot : snapshotRef . current ,
101+ watch
91102 } ;
92103} ;
0 commit comments