From c97ac6d2a6d3b4e5a60c8fcf7288f2cdee15e620 Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: Mon, 28 Oct 2019 04:49:32 +0100 Subject: [PATCH 1/2] complete refactor to hooks --- showcase/src/patterns/index.js | 170 +++++++++++++++++++++------------ 1 file changed, 108 insertions(+), 62 deletions(-) diff --git a/showcase/src/patterns/index.js b/showcase/src/patterns/index.js index a4a5ae5..fb18d7d 100644 --- a/showcase/src/patterns/index.js +++ b/showcase/src/patterns/index.js @@ -1,23 +1,39 @@ -import React, { Component, useState } from 'react' +import React, { + useState, + useCallback, + useLayoutEffect, + useContext, + useMemo, + createContext +} from 'react' + import mojs from 'mo-js' import { generateRandomNumber } from '../utils/generateRandomNumber' import styles from './index.css' /** ==================================== - * 🔰HOC -Higher Order Component for Animation -==================================== **/ -const withClapAnimation = WrappedComponent => { - class WithClapAnimation extends Component { - state = { - animationTimeline: new mojs.Timeline() - } + * 🔰Hook + Hook for Animation + ==================================== **/ + +const useClapAnimation = ({ + duration: tlDuration, + bounceEl, + fadeEl, + burstEl +}) => { + const [animationTimeline, setAnimationTimeline] = useState( + new mojs.Timeline() + ) - componentDidMount () { - const tlDuration = 300 + useLayoutEffect( + () => { + if (!bounceEl || !fadeEl || !burstEl) { + return + } const triangleBurst = new mojs.Burst({ - parent: '#clap', + parent: burstEl, radius: { 50: 95 }, count: 5, angle: 30, @@ -36,7 +52,7 @@ const withClapAnimation = WrappedComponent => { }) const circleBurst = new mojs.Burst({ - parent: '#clap', + parent: burstEl, radius: { 50: 75 }, angle: 25, duration: tlDuration, @@ -51,7 +67,7 @@ const withClapAnimation = WrappedComponent => { }) const countAnimation = new mojs.Html({ - el: '#clapCount', + el: bounceEl, isShowStart: false, isShowEnd: true, y: { 0: -30 }, @@ -64,7 +80,7 @@ const withClapAnimation = WrappedComponent => { }) const countTotalAnimation = new mojs.Html({ - el: '#clapCountTotal', + el: fadeEl, isShowStart: false, isShowEnd: true, opacity: { 0: 1 }, @@ -74,60 +90,71 @@ const withClapAnimation = WrappedComponent => { }) const scaleButton = new mojs.Html({ - el: '#clap', + el: burstEl, duration: tlDuration, scale: { 1.3: 1 }, easing: mojs.easing.out }) - const clap = document.getElementById('clap') - clap.style.transform = 'scale(1, 1)' - this.state.animationTimeline.add([ + if (typeof burstEl === 'string') { + clap.style.transform = 'scale(1, 1)' + const el = document.getElementById(id) + el.style.transform = 'scale(1, 1)' + } else { + burstEl.style.transform = 'scale(1, 1)' + } + + const updatedAnimationTimeline = animationTimeline.add([ countAnimation, countTotalAnimation, scaleButton, circleBurst, triangleBurst ]) - } - render () { - return ( - - ) - } - } - - WithClapAnimation.displayName = `WithClapAnimation(${getDisplayName( - WrappedComponent - )})` - - return WithClapAnimation -} + setAnimationTimeline(updatedAnimationTimeline) + }, + [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl] + ) -function getDisplayName (WrappedComponent) { - return WrappedComponent.displayName || WrappedComponent.name || 'Component' + return animationTimeline } - /** ==================================== - * 🔰 MediumClap -==================================== **/ + * 🔰 MediumClap + ==================================== **/ const initialState = { count: 0, countTotal: generateRandomNumber(500, 10000), isClicked: false } -const MediumClap = ({ animationTimeline }) => { +const MediumClapContext = createContext() +const { Provider } = MediumClapContext + +const MediumClap = ({ children }) => { const MAXIMUM_USER_CLAP = 50 const [clapState, setClapState] = useState(initialState) const { count, countTotal, isClicked } = clapState + const [{ clapRef, clapCountRef, clapTotalRef }, setRefState] = useState({}) + + const setRef = useCallback(node => { + if (node !== null) { + setRefState(prevRefState => ({ + ...prevRefState, + [node.dataset.refkey]: node + })) + } + }, []) + + const animationTimeline = useClapAnimation({ + duration: 300, + bounceEl: clapCountRef, + fadeEl: clapTotalRef, + burstEl: clapRef + }) + const handleClapClick = () => { - // 👉 prop from HOC animationTimeline.replay() setClapState({ @@ -137,21 +164,39 @@ const MediumClap = ({ animationTimeline }) => { }) } + const memoizedValue = useMemo( + () => ({ + count, + countTotal, + isClicked, + setRef + }), + [count, countTotal, isClicked, setRef] + ) + return ( - + + + ) } /** ==================================== - * 🔰SubComponents -Smaller Component used by -==================================== **/ + * 🔰SubComponents + Smaller Component used by + ==================================== **/ -const ClapIcon = ({ isClicked }) => { +const ClapIcon = () => { + const { isClicked } = useContext(MediumClapContext) return ( { ) } -const ClapCount = ({ count }) => { +const ClapCount = () => { + const { count, setRef } = useContext(MediumClapContext) return ( - + +{count} ) } -const CountTotal = ({ countTotal }) => { +const CountTotal = () => { + const { countTotal, setRef } = useContext(MediumClapContext) return ( - + {countTotal} ) } /** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API -==================================== **/ + * 🔰USAGE + Below's how a potential user + may consume the component API + ==================================== **/ const Usage = () => { - const AnimatedMediumClap = withClapAnimation(MediumClap) - return + return } export default Usage From eebd170316c74ac8d09137a36f2c68766e578b8e Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: Tue, 7 Jan 2020 06:08:34 +0100 Subject: [PATCH 2/2] fix: remove context object --- showcase/src/patterns/index.js | 224 +++++++++++++++------------------ 1 file changed, 98 insertions(+), 126 deletions(-) diff --git a/showcase/src/patterns/index.js b/showcase/src/patterns/index.js index fb18d7d..4217c63 100644 --- a/showcase/src/patterns/index.js +++ b/showcase/src/patterns/index.js @@ -1,11 +1,4 @@ -import React, { - useState, - useCallback, - useLayoutEffect, - useContext, - useMemo, - createContext -} from 'react' +import React, { useState, useCallback, useLayoutEffect } from 'react' import mojs from 'mo-js' import { generateRandomNumber } from '../utils/generateRandomNumber' @@ -26,96 +19,93 @@ const useClapAnimation = ({ new mojs.Timeline() ) - useLayoutEffect( - () => { - if (!bounceEl || !fadeEl || !burstEl) { - return - } + useLayoutEffect(() => { + if (!bounceEl || !fadeEl || !burstEl) { + return + } - const triangleBurst = new mojs.Burst({ - parent: burstEl, - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: burstEl, - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: bounceEl, - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, + const triangleBurst = new mojs.Burst({ + parent: burstEl, + radius: { 50: 95 }, + count: 5, + angle: 30, + children: { + shape: 'polygon', + radius: { 6: 0 }, + scale: 1, + stroke: 'rgba(211,84,0 ,0.5)', + strokeWidth: 2, + angle: 210, + delay: 30, + speed: 0.2, + easing: mojs.easing.bezier(0.1, 1, 0.3, 1), duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: fadeEl, - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: burstEl, - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.out - }) - - if (typeof burstEl === 'string') { - clap.style.transform = 'scale(1, 1)' - const el = document.getElementById(id) - el.style.transform = 'scale(1, 1)' - } else { - burstEl.style.transform = 'scale(1, 1)' } + }) - const updatedAnimationTimeline = animationTimeline.add([ - countAnimation, - countTotalAnimation, - scaleButton, - circleBurst, - triangleBurst - ]) - - setAnimationTimeline(updatedAnimationTimeline) - }, - [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl] - ) + const circleBurst = new mojs.Burst({ + parent: burstEl, + radius: { 50: 75 }, + angle: 25, + duration: tlDuration, + children: { + shape: 'circle', + fill: 'rgba(149,165,166 ,0.5)', + delay: 30, + speed: 0.2, + radius: { 3: 0 }, + easing: mojs.easing.bezier(0.1, 1, 0.3, 1) + } + }) + + const countAnimation = new mojs.Html({ + el: bounceEl, + isShowStart: false, + isShowEnd: true, + y: { 0: -30 }, + opacity: { 0: 1 }, + duration: tlDuration + }).then({ + opacity: { 1: 0 }, + y: -80, + delay: tlDuration / 2 + }) + + const countTotalAnimation = new mojs.Html({ + el: fadeEl, + isShowStart: false, + isShowEnd: true, + opacity: { 0: 1 }, + delay: (3 * tlDuration) / 2, + duration: tlDuration, + y: { 0: -3 } + }) + + const scaleButton = new mojs.Html({ + el: burstEl, + duration: tlDuration, + scale: { 1.3: 1 }, + easing: mojs.easing.out + }) + + if (typeof burstEl === 'string') { + clap.style.transform = 'scale(1, 1)' + const el = document.getElementById(id) + el.style.transform = 'scale(1, 1)' + } else { + burstEl.style.transform = 'scale(1, 1)' + } + + const updatedAnimationTimeline = animationTimeline.add([ + countAnimation, + countTotalAnimation, + scaleButton, + circleBurst, + triangleBurst + ]) + + setAnimationTimeline(updatedAnimationTimeline) + }, [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl]) return animationTimeline } @@ -128,10 +118,7 @@ const initialState = { isClicked: false } -const MediumClapContext = createContext() -const { Provider } = MediumClapContext - -const MediumClap = ({ children }) => { +const MediumClap = () => { const MAXIMUM_USER_CLAP = 50 const [clapState, setClapState] = useState(initialState) const { count, countTotal, isClicked } = clapState @@ -164,29 +151,17 @@ const MediumClap = ({ children }) => { }) } - const memoizedValue = useMemo( - () => ({ - count, - countTotal, - isClicked, - setRef - }), - [count, countTotal, isClicked, setRef] - ) - return ( - - - + ) } @@ -195,8 +170,7 @@ const MediumClap = ({ children }) => { Smaller Component used by ==================================== **/ -const ClapIcon = () => { - const { isClicked } = useContext(MediumClapContext) +const ClapIcon = ({ isClicked }) => { return ( { ) } -const ClapCount = () => { - const { count, setRef } = useContext(MediumClapContext) +const ClapCount = ({ count, setRef }) => { return ( +{count} ) } -const CountTotal = () => { - const { countTotal, setRef } = useContext(MediumClapContext) +const CountTotal = ({ countTotal, setRef }) => { return ( {countTotal}