Pārlūkot izejas kodu

@uppy/dashboard: refactor to stable lifecycle method (#4999)

Antoine du Hamel 1 gadu atpakaļ
vecāks
revīzija
96dc19766c
1 mainītis faili ar 62 papildinājumiem un 75 dzēšanām
  1. 62 75
      packages/@uppy/dashboard/src/components/Slide.jsx

+ 62 - 75
packages/@uppy/dashboard/src/components/Slide.jsx

@@ -1,4 +1,5 @@
-import {  cloneElement, Component, toChildArray  } from 'preact'
+import { cloneElement, toChildArray } from 'preact'
+import { useEffect, useState, useRef } from 'preact/hooks'
 import classNames from 'classnames'
 
 const transitionName = 'uppy-transition-slideDownUp'
@@ -13,87 +14,73 @@ const duration = 250
  * but it should be simple to extend this for any type of single-element
  * transition by setting the CSS name and duration as props.
  */
-class Slide extends Component {
-  constructor (props) {
-    super(props)
+function Slide ({ children }) {
+  const [cachedChildren, setCachedChildren] = useState(null);
+  const [className, setClassName] = useState('');
+  const enterTimeoutRef = useRef();
+  const leaveTimeoutRef = useRef();
+  const animationFrameRef = useRef();
+
+  const handleEnterTransition = () => {
+    setClassName(`${transitionName}-enter`);
+
+    cancelAnimationFrame(animationFrameRef.current);
+    clearTimeout(leaveTimeoutRef.current);
+    leaveTimeoutRef.current = undefined;
+
+    animationFrameRef.current = requestAnimationFrame(() => {
+      setClassName(`${transitionName}-enter ${transitionName}-enter-active`);
+
+      enterTimeoutRef.current = setTimeout(() => {
+        setClassName('');
+      }, duration);
+    });
+  };
+
+  const handleLeaveTransition = () => {
+    setClassName(`${transitionName}-leave`);
+
+    cancelAnimationFrame(animationFrameRef.current);
+    clearTimeout(enterTimeoutRef.current);
+    enterTimeoutRef.current = undefined;
+
+    animationFrameRef.current = requestAnimationFrame(() => {
+      setClassName(`${transitionName}-leave ${transitionName}-leave-active`);
+
+      leaveTimeoutRef.current = setTimeout(() => {
+        setCachedChildren(null);
+        setClassName('');
+      }, duration);
+    });
+  };
+
+  useEffect(() => {
+    const child = toChildArray(children)[0];
+    if (cachedChildren === child) return;
 
-    this.state = {
-      cachedChildren: null,
-      className: '',
-    }
-  }
-
-  // TODO: refactor to stable lifecycle method
-  // eslint-disable-next-line
-  componentWillUpdate (nextProps) {
-    const { cachedChildren } = this.state
-    const child = toChildArray(nextProps.children)[0]
-
-    if (cachedChildren === child) return null
-
-    const patch = {
-      cachedChildren: child,
-    }
-
-    // Enter transition
     if (child && !cachedChildren) {
-      patch.className = `${transitionName}-enter`
-
-      cancelAnimationFrame(this.animationFrame)
-      clearTimeout(this.leaveTimeout)
-      this.leaveTimeout = undefined
-
-      this.animationFrame = requestAnimationFrame(() => {
-        // Force it to render before we add the active class
-        // this.base.getBoundingClientRect()
-
-        this.setState({
-          className: `${transitionName}-enter ${transitionName}-enter-active`,
-        })
-
-        this.enterTimeout = setTimeout(() => {
-          this.setState({ className: '' })
-        }, duration)
-      })
+      handleEnterTransition();
+    } else if (cachedChildren && !child && !leaveTimeoutRef.current) {
+      handleLeaveTransition();
     }
 
-    // Leave transition
-    if (cachedChildren && !child && this.leaveTimeout === undefined) {
-      patch.cachedChildren = cachedChildren
-      patch.className = `${transitionName}-leave`
-
-      cancelAnimationFrame(this.animationFrame)
-      clearTimeout(this.enterTimeout)
-      this.enterTimeout = undefined
-      this.animationFrame = requestAnimationFrame(() => {
-        this.setState({
-          className: `${transitionName}-leave ${transitionName}-leave-active`,
-        })
-
-        this.leaveTimeout = setTimeout(() => {
-          this.setState({
-            cachedChildren: null,
-            className: '',
-          })
-        }, duration)
-      })
-    }
+    setCachedChildren(child);
+  }, [children, cachedChildren]); // Dependency array to trigger effect on children change
 
-    // eslint-disable-next-line
-    this.setState(patch)
-  }
 
-  render () {
-    const { cachedChildren, className } = this.state
+  useEffect(() => {
+    return () => {
+      clearTimeout(enterTimeoutRef.current);
+      clearTimeout(leaveTimeoutRef.current);
+      cancelAnimationFrame(animationFrameRef.current);
+    };
+  }, []); // Cleanup useEffect
 
-    if (!cachedChildren) {
-      return null
-    }
+  if (!cachedChildren) return null;
 
-    return cloneElement(cachedChildren, {
-      className: classNames(className, cachedChildren.props.className),
-    })
-  }
-}
+  return cloneElement(cachedChildren, {
+    className: classNames(className, cachedChildren.props.className),
+  });
+};
 
 export default Slide