Skip to content

Commit

Permalink
feat: optimize performance
Browse files Browse the repository at this point in the history
  • Loading branch information
liaoxuan committed Nov 17, 2023
1 parent aa9be57 commit 942a253
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 72 deletions.
56 changes: 15 additions & 41 deletions lib/PullToRefreshify.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { getScrollTop } from "./utils/getScrollTop";
import type { PullStatus, PullToRefreshifyProps } from "./types";
import { Events } from "./utils/events";

export function PullToRefreshify({
export const PullToRefreshify = ({
className,
style,
animationDuration = 300,
Expand All @@ -23,64 +23,38 @@ export function PullToRefreshify({
prefixCls = "pull-to-refreshify",
renderText,
children,
}: PullToRefreshifyProps) {
const { ref: pullRef, scrollParent } = useScrollParent();
}: PullToRefreshifyProps) => {
const [pullRef, scrollParentRef] = useScrollParent();
const unmountedRef = useUnmountedRef();
const [{ offsetY, duration, status }, setState] = useState<{
duration: number;
offsetY: number;
status: PullStatus;
}>(
const [[offsetY, duration, status], setState] = useState<
[number, number, PullStatus]
>(
refreshing
? {
duration: animationDuration,
offsetY: headHeight,
status: "refreshing",
}
: {
offsetY: 0,
duration: 0,
status: "normal",
}
? [headHeight, animationDuration, "refreshing"]
: [0, 0, "normal"]
);

const dispatch = (status: PullStatus, dragOffsetY = 0) => {
switch (status) {
case "pulling":
case "canRelease":
setState({
status: status,
duration: 0,
offsetY: dragOffsetY,
});
setState([dragOffsetY, 0, status]);
break;

case "refreshing":
setState({
status: status,
duration: animationDuration,
offsetY: headHeight,
});
setState([headHeight, animationDuration, status]);
break;

case "complete":
setState({
status: status,
duration: animationDuration,
offsetY: headHeight,
});
setState([headHeight, animationDuration, status]);
if (unmountedRef.current) return;
setTimeout(() => {
dispatch("normal");
}, completeDelay);
break;

default:
setState({
status: status,
duration: animationDuration,
offsetY: 0,
});
setState([0, animationDuration, status]);
}
};

Expand All @@ -90,15 +64,15 @@ export function PullToRefreshify({
}, [refreshing]);

// Handle darg events
const { ref: dragRef } = useDrag({
const dragRef = useDrag({
onDragMove: (event, { offsetY: dragOffsetY }) => {
if (
// Not set onRefresh event
!onRefresh ||
// Pull up
dragOffsetY <= 0 ||
// Not scrolled to top
(dragOffsetY > 0 && getScrollTop(scrollParent) > 0) ||
(dragOffsetY > 0 && getScrollTop(scrollParentRef.current) > 0) ||
// Refreshing state has been triggered
["refreshing", "complete"].includes(status) ||
disabled
Expand Down Expand Up @@ -191,4 +165,4 @@ export function PullToRefreshify({
</div>
</div>
);
}
};
14 changes: 7 additions & 7 deletions lib/utils/useDrag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ function isMouseEvent(e: DragEvent): e is MouseEvent {
return e && !("touches" in e);
}

export function useDrag({
export const useDrag = ({
onDragStart,
onDragMove,
onDragEnd,
}: {
onDragStart?: (event: DragEvent, dragState: DragState) => void;
onDragMove?: (event: DragEvent, dragState: DragState) => boolean;
onDragEnd?: (event: DragEvent, dragState: DragState) => void;
}) {
}) => {
const ref = useRef<any>(null);
const onDragStartRef = useLatest(onDragStart);
const onDragMoveRef = useLatest(onDragMove);
Expand All @@ -41,9 +41,7 @@ export function useDrag({

if (!dragEl) return;

let dragState = {
...initialDragState,
};
let dragState: DragState;
let isStart = false;

const initDragState = () => {
Expand All @@ -52,6 +50,8 @@ export function useDrag({
};
};

initDragState();

const handleTouchstart = ((event: DragEvent) => {
isStart = true;
initDragState();
Expand Down Expand Up @@ -123,5 +123,5 @@ export function useDrag({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return { ref };
}
return ref;
};
4 changes: 2 additions & 2 deletions lib/utils/useFirstMountState.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useRef } from "react";

export function useFirstMountState(): boolean {
export const useFirstMountState = (): boolean => {
const isFirst = useRef(true);

if (isFirst.current) {
Expand All @@ -10,4 +10,4 @@ export function useFirstMountState(): boolean {
}

return isFirst.current;
}
};
53 changes: 31 additions & 22 deletions lib/utils/useScrollParent.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useEffect, useRef, useState } from "react";
import { useEffect, useRef } from "react";
import { getScrollTop } from "./getScrollTop";
import { Events } from "./events";

function getScrollParent(node: Element) {
const getScrollParent = (node: Element) => {
while (node && node.parentNode && node.parentNode !== document.body) {
const computedStyle = window.getComputedStyle(node);
if (
Expand All @@ -19,42 +19,39 @@ function getScrollParent(node: Element) {
}

return window;
}
};

export function useScrollParent() {
const ref = useRef<any>();
const [scrollParent, setScrollParent] =
useState<ReturnType<typeof getScrollParent>>(window);
export const useScrollParent = () => {
const pullRef = useRef<any>();
const touchstartYRef = useRef(0);
const scrollParentRef = useRef<ReturnType<typeof getScrollParent>>(window);
const unbindScrollParentEvents = useRef(() => {});

// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => {
setScrollParent(getScrollParent(ref.current));
});

// Handle the scroll parent's touch events
useEffect(() => {
let touchstartY = 0;
const bindScrollParentEvents = (
scrollParent: ReturnType<typeof getScrollParent>
) => {
touchstartYRef.current = 0;

const handleTouchstart = ((event: TouchEvent) => {
const touch = event.touches[0];
touchstartY = touch.pageY;
touchstartYRef.current = touch.pageY;
}) as EventListener;

const handleTouchmove = ((event: TouchEvent) => {
const touch = event.touches[0];
const currentY = touch.pageY;
if (
currentY - touchstartY > 0 &&
currentY - touchstartYRef.current > 0 &&
event.cancelable &&
getScrollTop(scrollParent) === 0 &&
ref.current?.contains(event.target)
pullRef.current?.contains(event.target)
) {
event.preventDefault();
}
}) as EventListener;

const handleTouchend = (() => {
touchstartY = 0;
touchstartYRef.current = 0;
}) as EventListener;

Events.on(scrollParent, "touchstart", handleTouchstart);
Expand All @@ -68,7 +65,19 @@ export function useScrollParent() {
Events.off(scrollParent, "touchend", handleTouchend);
Events.off(scrollParent, "touchcancel", handleTouchend);
};
}, [scrollParent]);
};

// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => {
const nextScrollParent = getScrollParent(pullRef.current);

if (nextScrollParent !== scrollParentRef.current) {
unbindScrollParentEvents.current();
unbindScrollParentEvents.current =
bindScrollParentEvents(nextScrollParent);
scrollParentRef.current = nextScrollParent;
}
});

return { ref, scrollParent };
}
return [pullRef, scrollParentRef] as const;
};

0 comments on commit 942a253

Please sign in to comment.