瀏覽代碼

fix: props children update carousel did no rerender and ref.play should have the highest priority (#1495)

* fix: props children update carousel did no rerender and ref.play should have the highest priority

* fix: props children update carousel did no rerender and ref.play should have the highest priority
YannLynn 2 年之前
父節點
當前提交
58bb122d0b

+ 8 - 2
packages/semi-foundation/carousel/foundation.ts

@@ -18,6 +18,11 @@ class CarouselFoundation<P = Record<string, any>, S = Record<string, any>> exten
     }
 
     _interval = null;
+    _forcePlay = false;
+
+    setForcePlay(forcePlay: boolean) {
+        this._forcePlay = forcePlay;
+    }
 
     play(interval: number): void {
         if (this._interval) {
@@ -105,7 +110,7 @@ class CarouselFoundation<P = Record<string, any>, S = Record<string, any>> exten
     getSwitchingTime(): number {
         const { autoPlay, speed } = this.getProps(); 
         const autoPlayType = typeof autoPlay;
-        if (autoPlayType === 'boolean' && autoPlay){
+        if (autoPlayType === 'boolean'){ 
             return numbers.DEFAULT_INTERVAL + speed;
         }
         if (isObject(autoPlay)){
@@ -121,7 +126,8 @@ class CarouselFoundation<P = Record<string, any>, S = Record<string, any>> exten
     handleAutoPlay(): void { 
         const { autoPlay } = this.getProps(); 
         const autoPlayType = typeof autoPlay;
-        if ((autoPlayType === 'boolean' && autoPlay) || isObject(autoPlay)){
+        // when user manually call the play function, force play
+        if ((autoPlayType === 'boolean' && autoPlay) || isObject(autoPlay) || this._forcePlay){
             this.play(this.getSwitchingTime());
         }
     }

+ 36 - 0
packages/semi-ui/carousel/_story/carousel.stories.jsx

@@ -474,6 +474,42 @@ slideDirection.story = {
   name: 'slide direction',
 };
 
+export const fix1482 = () => {
+  const [children, setChildren] = useState([1, 2]);
+  const carouselRef = React.useRef();
+
+  React.useEffect(() => {
+    setChildren([3, 4, 5]);
+  },[])
+
+  return (
+    <div 
+      onMouseEnter={() => {
+        console.log('onMouseEnter play');
+        carouselRef.current.play();
+      }}
+      onMouseLeave={() => {
+        console.log('onMouseLeave stop');
+        carouselRef.current.stop();
+      }}
+    >
+      <Carousel style={style} autoPlay={false} ref={carouselRef}>
+        {children.map((item, index)=>{
+          return (
+            <div style={contentPinkStyle} key={index}>
+              <h3>index{index}</h3>
+            </div>
+          )
+        })}
+      </Carousel>
+    </div>
+);
+}
+
+fix1482.story = {
+  name: 'fix-1482',
+};
+
 
 
 

+ 18 - 3
packages/semi-ui/carousel/index.tsx

@@ -1,5 +1,5 @@
 /* eslint-disable jsx-a11y/no-static-element-interactions */
-import React, { ReactNode, Children, ReactChild, ReactFragment, ReactPortal } from 'react';
+import React, { ReactNode, Children, ReactChild, ReactFragment, ReactPortal, isValidElement } from 'react';
 import cls from 'classnames';
 import PropTypes from 'prop-types';
 import BaseComponent from "../_base/baseComponent";
@@ -9,7 +9,7 @@ import CarouselFoundation, { CarouselAdapter } from '@douyinfe/semi-foundation/c
 import CarouselIndicator from './CarouselIndicator';
 import CarouselArrow from './CarouselArrow';
 import '@douyinfe/semi-foundation/carousel/carousel.scss';
-import { debounce } from 'lodash';
+import { debounce, isEqual, pick } from 'lodash';
 import isNullOrUndefined from '@douyinfe/semi-foundation/utils/isNullOrUndefined';
 
 export interface CarouselState {
@@ -110,15 +110,30 @@ class Carousel extends BaseComponent<CarouselProps, CarouselState> {
         this.handleAutoPlay();
     }
 
+    componentDidUpdate(prevProps: Readonly<CarouselProps>, prevState: Readonly<CarouselState>, snapshot?: any): void {
+        const prevChildrenKeys = React.Children.toArray(prevProps.children).map((child) =>
+            isValidElement(child) ? child.key : null
+        );
+        const nowChildrenKeys = React.Children.toArray(this.props.children).map((child) =>
+            isValidElement(child) ? child.key : null
+        );
+
+        if (!isEqual(prevChildrenKeys, nowChildrenKeys)) {
+            this.setState({ children: this.getChildren() });
+        }
+    }
+
     componentWillUnmount(): void {
         this.foundation.destroy();
     }
 
     play = (): void => {
+        this.foundation.setForcePlay(true);
         return this.foundation.handleAutoPlay();
     }
 
     stop = (): void => {
+        this.foundation.setForcePlay(false);
         return this.foundation.stop();
     };
 
@@ -142,7 +157,7 @@ class Carousel extends BaseComponent<CarouselProps, CarouselState> {
 
     handleMouseEnter = (): void => {
         const { autoPlay } = this.props;
-        if (typeof autoPlay !== 'object' || autoPlay.hoverToPause){
+        if ((autoPlay === true) || (typeof autoPlay === 'object' && autoPlay.hoverToPause)){
             this.foundation.stop();
         }
     }