From 290dab00f8630f96623677320d98169171ab107a Mon Sep 17 00:00:00 2001 From: J Scott Smith Date: Wed, 1 Nov 2023 22:39:36 -0700 Subject: [PATCH] chore: refactor ParallaxProvider to function component (#230) --- .../ParallaxProvider/ParallaxProvider.tsx | 81 +++++++-------- .../ParallaxProvider/index.test.tsx | 98 +++++++++---------- 2 files changed, 84 insertions(+), 95 deletions(-) diff --git a/src/components/ParallaxProvider/ParallaxProvider.tsx b/src/components/ParallaxProvider/ParallaxProvider.tsx index e0a25932..fe604189 100644 --- a/src/components/ParallaxProvider/ParallaxProvider.tsx +++ b/src/components/ParallaxProvider/ParallaxProvider.tsx @@ -1,56 +1,49 @@ -import React, { Component } from 'react'; +import React, { PropsWithChildren, useEffect, useRef } from 'react'; import { ParallaxContext } from '../../context/ParallaxContext'; -import { ParallaxController, ScrollAxis } from 'parallax-controller'; +import { ScrollAxis } from 'parallax-controller'; import { ParallaxProviderProps } from './types'; import { createController } from './helpers'; -export class ParallaxProvider extends Component { - static defaultProps = { - scrollAxis: ScrollAxis.vertical, - }; - - controller: ParallaxController | null; - - constructor(props: ParallaxProviderProps) { - super(props); - this.controller = createController({ - scrollAxis: props.scrollAxis, +export function ParallaxProvider( + props: PropsWithChildren +) { + const controller = useRef( + createController({ + scrollAxis: props.scrollAxis || ScrollAxis.vertical, scrollContainer: props.scrollContainer, disabled: props.isDisabled, - }); - } + }) + ); - componentDidUpdate(prevProps: ParallaxProviderProps) { - if ( - prevProps.scrollContainer !== this.props.scrollContainer && - this.props.scrollContainer - ) { - this.controller?.updateScrollContainer(this.props.scrollContainer); + // update scroll container + useEffect(() => { + if (props.scrollContainer && controller.current) { + controller.current.updateScrollContainer(props.scrollContainer); } + }, [props.scrollContainer, controller.current]); - if (prevProps.isDisabled !== this.props.isDisabled) { - if (this.props.isDisabled) { - this.controller?.disableParallaxController(); - } - if (!this.props.isDisabled) { - this.controller?.enableParallaxController(); - } + // disable/enable parallax + useEffect(() => { + if (props.isDisabled && controller.current) { + controller.current.disableParallaxController(); } - } - - componentWillUnmount() { - // @ts-ignore - this.controller = this.controller.destroy(); - } - - render() { - const { children } = this.props; - return ( - // @ts-ignore - - {children} - - ); - } + if (!props.isDisabled && controller.current) { + controller.current.enableParallaxController(); + } + }, [props.isDisabled, controller.current]); + + // remove the controller when unmounting + useEffect(() => { + return () => { + controller?.current && controller?.current.destroy(); + controller.current = null; + }; + }, []); + + return ( + + {props.children} + + ); } diff --git a/src/components/ParallaxProvider/index.test.tsx b/src/components/ParallaxProvider/index.test.tsx index af769639..8fa04b66 100644 --- a/src/components/ParallaxProvider/index.test.tsx +++ b/src/components/ParallaxProvider/index.test.tsx @@ -84,59 +84,53 @@ describe('A ', () => { }); it('to destroy the controller when unmounting', () => { - const node = document.createElement('div'); + let parallaxController: ParallaxController | null = null; + const AddDestroySpy = () => { + parallaxController = useParallaxController(); + if (parallaxController) { + jest.spyOn(parallaxController, 'destroy'); + } + return null; + }; - let instance; - ReactDOM.render( - (instance = ref)}> -
- , - node + const screen = render( + + + ); - // @ts-ignore - instance.controller.destroy = jest.fn(); - // @ts-ignore - const spy = instance.controller.destroy; - - ReactDOM.unmountComponentAtNode(node); - - expect(spy).toBeCalled(); + screen.unmount(); + // @ts-expect-error + expect(parallaxController?.destroy).toBeCalled(); }); it('to update the scroll container when receiving a new container el', () => { - const node = document.createElement('div'); - let instance; - let providerInstance; - - class StateChanger extends React.Component { - state = { el: undefined }; - render() { - return ( - (providerInstance = ref)} - > -
- - ); - } - } + let parallaxController: ParallaxController | null = null; - ReactDOM.render( (instance = ref)} />, node); + const AddUpdateSpy = () => { + parallaxController = useParallaxController(); + if (parallaxController) { + jest.spyOn(parallaxController, 'updateScrollContainer'); + } + return null; + }; const el = document.createElement('div'); + const screen = render( + + + + ); - // @ts-ignore - providerInstance.controller.updateScrollContainer = jest.fn(); - // @ts-ignore - const spy = providerInstance.controller.updateScrollContainer; - // @ts-ignore - instance.setState({ el }); - - ReactDOM.unmountComponentAtNode(node); + screen.rerender( + + + + ); - expect(spy).toBeCalledWith(el); + screen.unmount(); + // @ts-expect-error + expect(parallaxController?.updateScrollContainer).toBeCalledWith(el); }); // NOTE: I think this test can be removed @@ -150,10 +144,15 @@ describe('A ', () => { const node2 = document.createElement('div'); const render = (node: HTMLDivElement) => { - let instance; + let instance: ParallaxController | null = null; + const GetInstance = () => { + instance = useParallaxController(); + return null; + }; ReactDOM.render( - (instance = ref)}> -
+ // @ts-ignore + + , node ); @@ -162,19 +161,16 @@ describe('A ', () => { // first instance mounted const instance1 = render(node1); - // @ts-ignore - expect(instance1.controller).toBeInstanceOf(ParallaxController); + expect(instance1).toBeInstanceOf(ParallaxController); // second instance mounted const instance2 = render(node2); - // @ts-ignore - expect(instance2.controller).toBeInstanceOf(ParallaxController); + expect(instance2).toBeInstanceOf(ParallaxController); // unmount first instance ReactDOM.unmountComponentAtNode(node1); // this must still be defined - // @ts-ignore - expect(instance2.controller).toBeInstanceOf(ParallaxController); + expect(instance2).toBeInstanceOf(ParallaxController); }); });