From edbd72a67955dc3c04f1f2f2668d50a925693a76 Mon Sep 17 00:00:00 2001 From: Hasan Genc Date: Wed, 30 Dec 2020 13:02:57 +0300 Subject: [PATCH] fix re-render previous nodes on state change --- src/components/carousel/defaultProps.ts | 5 +- src/components/carousel/index.tsx | 90 +++++++++++++----------- src/helpers/index.ts | 5 ++ src/hooks/index.ts | 8 +++ src/styles/slider/styles.module.css.d.ts | 16 ++--- src/styles/styles.module.css.d.ts | 34 ++++----- 6 files changed, 89 insertions(+), 69 deletions(-) diff --git a/src/components/carousel/defaultProps.ts b/src/components/carousel/defaultProps.ts index 0c24c57..6088744 100644 --- a/src/components/carousel/defaultProps.ts +++ b/src/components/carousel/defaultProps.ts @@ -13,7 +13,6 @@ export const defaultProps: Required = { useArrowKeys: false, a11y: {}, dynamic: false, - beforeChange: null, - afterChange: null, - extraItems: [], + paginationCallback: null, + pageCount: 0, }; diff --git a/src/components/carousel/index.tsx b/src/components/carousel/index.tsx index 6de9660..623c649 100644 --- a/src/components/carousel/index.tsx +++ b/src/components/carousel/index.tsx @@ -1,4 +1,10 @@ -import React, { useState, FunctionComponent, KeyboardEvent, useEffect } from 'react'; +import React, { + useState, + FunctionComponent, + KeyboardEvent, + useEffect, + useRef, +} from 'react'; import { Arrow } from '../arrow'; import { ItemProvider } from '../item'; import { @@ -13,6 +19,7 @@ import { import { SlideDirection, Item, ArrowKeys } from '../../types/carousel'; import { defaultProps } from './defaultProps'; import styles from '../../styles/styles.module.css'; +import { usePrevious } from '../../hooks'; export const Carousel: FunctionComponent = (userProps: CarouselProps) => { const props: Required = { ...defaultProps, ...userProps }; @@ -29,40 +36,35 @@ export const Carousel: FunctionComponent = (userProps: CarouselPr const [showArrow, setShowArrow] = useState( getShowArrow(props.children.length, props.show, props.infinite, current), ); - const [dynamicElements, setDynamicElements] = useState(userProps.children); - - /*useEffect(() => { - if (Array.isArray(props.extraItems) && props.extraItems.length > 0) { - const elements = dynamicElements.length > 0 ? dynamicElements : props.children; - setDynamicElements(elements.concat(props.extraItems)); - } - }, [props.extraItems]);*/ - - useEffect(() => { - if (dynamicElements.length > props.children.length) { - slide(SlideDirection.Right, true); - } - }, [dynamicElements]); + const prevChildren = usePrevious(userProps.children); + const [page, setPage] = useState(0); + const itemsRef = useRef(initItems(props.children, props.slide, props.infinite)); + const isPaginating = useRef(false); if (props.dynamic) { useEffect(() => { - console.log({ - children: userProps.children, - dynamicElements, - }); - const newItems = updateNodes( - items, - dynamicElements, + itemsRef.current, + props.children, + prevChildren, props.slide, props.infinite, ); - console.log({ newItems }); + setItems(newItems); - }, [userProps.children, dynamicElements]); + itemsRef.current = newItems; + if ( + page < props.pageCount && + prevChildren && + prevChildren?.length < props.children.length + ) { + slide(SlideDirection.Right); + setPage(page + 1); + } + }, [props.children]); } - const slide = async (direction: SlideDirection, cb?: boolean) => { + const slide = (direction: SlideDirection) => { if ( animation.isSliding || (direction === SlideDirection.Right && !showArrow.right) || @@ -71,19 +73,18 @@ export const Carousel: FunctionComponent = (userProps: CarouselPr return; } - const dynamicElementsExists = dynamicElements.length > 0; - - if (!cb && props.beforeChange) { - const extraItems = await props.beforeChange(direction); - if (Array.isArray(extraItems) && extraItems.length > 0) { - const elements = - dynamicElements.length > 0 ? dynamicElements : props.children; - setDynamicElements(elements.concat(extraItems)); - return; - } + if ( + props.paginationCallback && + direction === SlideDirection.Right && + page < props.pageCount - 1 && + !isPaginating.current + ) { + isPaginating.current = true; + props.paginationCallback(direction); + return; } - const elements = dynamicElementsExists ? dynamicElements : props.children; + const elements = props.children; const next = getCurrent(current, props.slide, elements.length, direction); const rotated = props.infinite @@ -91,6 +92,7 @@ export const Carousel: FunctionComponent = (userProps: CarouselPr : items; if (props.infinite && direction === SlideDirection.Right) { setItems(rotated); + itemsRef.current = rotated; } setAnimation({ transform: @@ -102,7 +104,13 @@ export const Carousel: FunctionComponent = (userProps: CarouselPr setShowArrow(getShowArrow(elements.length, props.show, props.infinite, next)); setTimeout(() => { if (props.infinite) { - setItems(cleanItems(rotated, props.slide, direction)); + const cleanedItems = cleanItems( + direction === SlideDirection.Right ? itemsRef.current : rotated, + props.slide, + direction, + ); + setItems(cleanedItems); + itemsRef.current = cleanedItems; } setAnimation({ transform: props.infinite @@ -113,6 +121,7 @@ export const Carousel: FunctionComponent = (userProps: CarouselPr isSliding: false, }); }, props.transition * 1_0_0_0); + isPaginating.current = false; }; const widthCallBack = (calculatedWidth: number) => { @@ -164,7 +173,7 @@ export const Carousel: FunctionComponent = (userProps: CarouselPr Promise) | null; - afterChange?: ((direction: SlideDirection) => void) | null; - extraItems?: any[]; + paginationCallback?: ((direction: SlideDirection) => any) | null; + pageCount?: number; } export interface CarouselState { diff --git a/src/helpers/index.ts b/src/helpers/index.ts index 581302a..949b971 100644 --- a/src/helpers/index.ts +++ b/src/helpers/index.ts @@ -147,9 +147,14 @@ export function getOuterWidth(el: HTMLElement) { export function updateNodes( oldItems: Item[], newItems: Item[], + prevChildren: Item[] | undefined, slide: number, infinite: boolean, ): Item[] { + if (prevChildren && prevChildren.length < newItems.length) { + return initItems(newItems, slide, infinite); + } + const matchedItems = oldItems.map((oldItem) => { return newItems.find((newItem) => oldItem.key === newItem.key) as Item; }); diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 505a9d0..09a76b5 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -13,3 +13,11 @@ export const useWindowWidthChange = (callBack: (changed: number) => any) => { }, []); return; }; + +export const usePrevious = (value: T) => { + const ref = useRef(); + useEffect(() => { + ref.current = value; + }); + return ref.current; +} diff --git a/src/styles/slider/styles.module.css.d.ts b/src/styles/slider/styles.module.css.d.ts index 2966e8b..0fb4e6f 100644 --- a/src/styles/slider/styles.module.css.d.ts +++ b/src/styles/slider/styles.module.css.d.ts @@ -1,10 +1,10 @@ -export const sliderBase: string; -export const slider: string; -export const sliding: string; +export const sliderBase: string +export const slider: string +export const sliding: string interface Namespace { - sliderBase: string; - slider: string; - sliding: string; + "sliderBase": string, + "slider": string, + "sliding": string, } -declare const stylesModule: Namespace; -export default stylesModule; +declare const stylesModule: Namespace +export default stylesModule diff --git a/src/styles/styles.module.css.d.ts b/src/styles/styles.module.css.d.ts index 08bed51..7ed2c12 100644 --- a/src/styles/styles.module.css.d.ts +++ b/src/styles/styles.module.css.d.ts @@ -1,19 +1,19 @@ -export const carouselBase: string; -export const itemProvider: string; -export const itemContainer: string; -export const itemTracker: string; -export const carouselArrow: string; +export const carouselBase: string +export const itemProvider: string +export const itemContainer: string +export const itemTracker: string +export const carouselArrow: string interface Namespace { - carouselBase: string; - 'carousel-base': string; - itemProvider: string; - 'item-provider': string; - itemContainer: string; - 'item-container': string; - itemTracker: string; - 'item-tracker': string; - carouselArrow: string; - 'carousel-arrow': string; + "carouselBase": string, + "carousel-base": string, + "itemProvider": string, + "item-provider": string, + "itemContainer": string, + "item-container": string, + "itemTracker": string, + "item-tracker": string, + "carouselArrow": string, + "carousel-arrow": string, } -declare const stylesModule: Namespace; -export default stylesModule; +declare const stylesModule: Namespace +export default stylesModule