From 9d8b8e4741c8d1ce9962874c891c9c0c5629556d Mon Sep 17 00:00:00 2001 From: Marvin Frachet Date: Fri, 18 Sep 2020 15:04:32 +0200 Subject: [PATCH] Refactoring to hooks + new context API (#139) --- example/__snapshots__/storyshots.test.ts.snap | 60 +++++++-------- src/Placeholder.tsx | 12 +-- src/PlaceholderLine.tsx | 20 ++--- src/PlaceholderMedia.tsx | 20 ++--- src/animations/AnimationConsumer.tsx | 11 --- src/animations/Fade.tsx | 76 +++++++++---------- src/animations/Loader.tsx | 3 - src/animations/Progressive.tsx | 74 ++++++++---------- src/animations/Raw.tsx | 5 +- src/animations/Shine.tsx | 61 +++++++-------- src/animations/ShineOverlay.tsx | 66 ++++++++-------- src/animations/context.ts | 5 +- src/index.tsx | 2 +- src/tokens.tsx | 4 +- 14 files changed, 189 insertions(+), 230 deletions(-) delete mode 100644 src/animations/AnimationConsumer.tsx diff --git a/example/__snapshots__/storyshots.test.ts.snap b/example/__snapshots__/storyshots.test.ts.snap index 8ee4aa2..37d6198 100644 --- a/example/__snapshots__/storyshots.test.ts.snap +++ b/example/__snapshots__/storyshots.test.ts.snap @@ -1839,7 +1839,7 @@ exports[`Storyshots rn-placeholder Animations 1`] = ` } > @@ -1932,7 +1932,7 @@ exports[`Storyshots rn-placeholder Animations 1`] = ` } > @@ -8892,7 +8892,7 @@ exports[`Storyshots rn-placeholder Custom Line and Media 1`] = ` } > @@ -8987,7 +8987,7 @@ exports[`Storyshots rn-placeholder Custom Line and Media 1`] = ` } > @@ -18644,7 +18644,7 @@ exports[`Storyshots rn-placeholder Custom animations 1`] = ` } > @@ -23800,7 +23800,7 @@ exports[`Storyshots rn-placeholder Line and Media 1`] = ` } > @@ -23893,7 +23893,7 @@ exports[`Storyshots rn-placeholder Line and Media 1`] = ` } > @@ -25229,7 +25229,7 @@ exports[`Storyshots rn-placeholder Line and Media 1`] = ` } > @@ -26504,7 +26504,7 @@ exports[`Storyshots rn-placeholder Line and Media 1`] = ` } > @@ -26569,7 +26569,7 @@ exports[`Storyshots rn-placeholder Line and Media 1`] = ` } > @@ -27773,7 +27773,7 @@ exports[`Storyshots rn-placeholder Line and Media 1`] = ` } > diff --git a/src/Placeholder.tsx b/src/Placeholder.tsx index 90e1c54..067328e 100644 --- a/src/Placeholder.tsx +++ b/src/Placeholder.tsx @@ -3,7 +3,7 @@ import { StyleSheet, View, ViewProps } from "react-native"; import { Raw } from "./animations/Raw"; import { SIZES } from "./tokens"; -export interface IPlaceholder extends ViewProps { +export interface PlaceholderProps extends ViewProps { /* An optional component that animates the placeholder */ Animation?: React.ComponentType; /* An optional component to display on the left */ @@ -12,7 +12,7 @@ export interface IPlaceholder extends ViewProps { Right?: React.ComponentType; } -export const Placeholder: React.FC = ({ +export const Placeholder: React.FC = ({ children, style, Left, @@ -35,13 +35,13 @@ export const Placeholder: React.FC = ({ const styles = StyleSheet.create({ full: { - flex: 1 + flex: 1, }, left: { - marginRight: SIZES.normal + marginRight: SIZES.normal, }, right: { - marginLeft: SIZES.normal + marginLeft: SIZES.normal, }, - row: { flexDirection: "row", width: "100%" } + row: { flexDirection: "row", width: "100%" }, }); diff --git a/src/PlaceholderLine.tsx b/src/PlaceholderLine.tsx index 9658df9..c5ae5ae 100644 --- a/src/PlaceholderLine.tsx +++ b/src/PlaceholderLine.tsx @@ -1,9 +1,9 @@ import React from "react"; -import { StyleSheet, View, ViewProps } from "react-native"; -import { AnimationConsumer } from "./animations/AnimationConsumer"; +import { Animated, StyleSheet, View, ViewProps } from "react-native"; +import { useAnimation } from "./animations/context"; import { COLORS, SIZES } from "./tokens"; -export interface ILine extends ViewProps { +export interface PlaceholderLineProps extends ViewProps { /* The line height, default is 12 */ height?: number; /* The line color, default is #efefef */ @@ -16,12 +16,12 @@ export interface ILine extends ViewProps { style?: ViewProps["style"]; } -export const PlaceholderLine: React.FC = ({ +export const PlaceholderLine: React.FC = ({ height = SIZES.normal, color = COLORS.primary, width = 100, noMargin = false, - style + style, }) => { const backgroundColor = color; const borderRadius = height / 4; @@ -32,18 +32,20 @@ export const PlaceholderLine: React.FC = ({ borderRadius, height, marginBottom, - width: `${width}%` + width: `${width}%`, }; + const animationStyle = useAnimation(); + return ( - + ); }; const styles = StyleSheet.create({ line: { - overflow: "hidden" - } + overflow: "hidden", + }, }); diff --git a/src/PlaceholderMedia.tsx b/src/PlaceholderMedia.tsx index a0d87df..59bf3ee 100644 --- a/src/PlaceholderMedia.tsx +++ b/src/PlaceholderMedia.tsx @@ -1,9 +1,9 @@ import React from "react"; -import { StyleSheet, View, ViewProps } from "react-native"; -import { AnimationConsumer } from "./animations/AnimationConsumer"; +import { Animated, StyleSheet, View, ViewProps } from "react-native"; +import { useAnimation } from "./animations/context"; import { COLORS, SIZES } from "./tokens"; -export interface IMedia extends ViewProps { +export interface PlaceholderMediaProps extends ViewProps { /* The media size (height / width), default is 40 */ size?: number; /* Defines if the media is rounded or not, default is false */ @@ -14,28 +14,30 @@ export interface IMedia extends ViewProps { style?: ViewProps["style"]; } -export const PlaceholderMedia: React.FC = ({ +export const PlaceholderMedia: React.FC = ({ size = SIZES.xxl, isRound = false, color = COLORS.primary, - style + style, }) => { const computedStyles = { backgroundColor: color, borderRadius: isRound ? size / 2 : 3, height: size, - width: size + width: size, }; + const animationStyle = useAnimation(); + return ( - + ); }; const styles = StyleSheet.create({ media: { - overflow: "hidden" - } + overflow: "hidden", + }, }); diff --git a/src/animations/AnimationConsumer.tsx b/src/animations/AnimationConsumer.tsx deleted file mode 100644 index 260bb36..0000000 --- a/src/animations/AnimationConsumer.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from "react"; -import { Animated, ViewProps } from "react-native"; -import { Consumer } from "./context"; - -export const AnimationConsumer: React.FC = ({ children }) => ( - - {animationStyle => ( - {children} - )} - -); diff --git a/src/animations/Fade.tsx b/src/animations/Fade.tsx index 96f5faf..b27c776 100644 --- a/src/animations/Fade.tsx +++ b/src/animations/Fade.tsx @@ -1,64 +1,58 @@ -import React from "react"; +import React, { useRef } from "react"; import { Animated, ViewProps } from "react-native"; -import { Provider } from "./context"; +import { AnimationContext } from "./context"; const START_VALUE = 0.5; const END_VALUE = 1; const useNativeDriver = true; const isInteraction = false; -export interface IFade extends ViewProps { +export interface FadeProps extends ViewProps { /* Animation duration, default is 500 */ duration?: number; } -export class Fade extends React.Component { - public static defaultProps = { - duration: 500 - }; - - private animation: Animated.Value; - - constructor(props: IFade) { - super(props); - - this.animation = new Animated.Value(START_VALUE); - } - - public componentDidMount() { - this.start(); - } - - public render() { - const { children, style } = this.props; - const animationStyle = { - backgroundColor: "#dfdfdf", - height: "100%", - opacity: this.animation - }; - - return {children}; - } +export const Fade: React.FC = ({ + duration = 500, + children, + style, +}) => { + const animation = useRef(new Animated.Value(START_VALUE)); - private start() { - const { duration } = this.props; + const start = () => { Animated.sequence([ - Animated.timing(this.animation, { + Animated.timing(animation.current, { duration, isInteraction, toValue: END_VALUE, - useNativeDriver + useNativeDriver, }), - Animated.timing(this.animation, { + Animated.timing(animation.current, { duration, isInteraction, toValue: START_VALUE, - useNativeDriver - }) - ]).start(e => { + useNativeDriver, + }), + ]).start((e) => { if (e.finished) { - this.start(); + start(); } }); - } -} + }; + + React.useEffect(() => { + start(); + }, []); + + const animationStyle = { + backgroundColor: "#dfdfdf", + height: "100%", + opacity: animation.current, + }; + + return ( + + {children} + + ); +}; diff --git a/src/animations/Loader.tsx b/src/animations/Loader.tsx index 2b2cd39..284ed4f 100644 --- a/src/animations/Loader.tsx +++ b/src/animations/Loader.tsx @@ -5,18 +5,15 @@ import { StyleSheet, View } from "react-native"; -import { Provider } from "./context"; export const Loader: React.FC = ({ children, ...props }) => ( - {children} - ); const styles = StyleSheet.create({ diff --git a/src/animations/Progressive.tsx b/src/animations/Progressive.tsx index 1670432..e983152 100644 --- a/src/animations/Progressive.tsx +++ b/src/animations/Progressive.tsx @@ -1,59 +1,32 @@ -import React from "react"; +import React, { useRef } from "react"; import { Animated, StyleSheet, ViewProps } from "react-native"; -import { Provider } from "./context"; +import { AnimationContext } from "./context"; const START_VALUE = 0; const END_VALUE = 100; const DURATION = 750; const isInteraction = false; -export interface IProgressive extends ViewProps { +export interface ProgressiveProps extends ViewProps { color?: string; } -export class Progressive extends React.Component { - private animation: Animated.Value; +export const Progressive: React.FC = ({ + style, + color = "rgba(0,0,0,0.1)", + children, +}) => { + const animation = useRef(new Animated.Value(START_VALUE)); - constructor(props: IProgressive) { - super(props); - - this.animation = new Animated.Value(START_VALUE); - } - - public componentDidMount() { - this.start(); - } - - public render() { - const { children, style, color = "rgba(0,0,0,0.1)" } = this.props; - - const right = this.animation.interpolate({ - inputRange: [START_VALUE, END_VALUE], - outputRange: ["0%", "100%"], - }); - - return ( - - {children} - - ); - } - - private start() { + const start = () => { Animated.sequence([ - Animated.timing(this.animation, { + Animated.timing(animation.current, { duration: DURATION, isInteraction, toValue: END_VALUE, useNativeDriver: false, }), - Animated.timing(this.animation, { + Animated.timing(animation.current, { duration: DURATION, isInteraction, toValue: START_VALUE, @@ -61,11 +34,28 @@ export class Progressive extends React.Component { }), ]).start((e) => { if (e.finished) { - this.start(); + start(); } }); - } -} + }; + + React.useEffect(() => { + start(); + }, []); + + const right = animation.current.interpolate({ + inputRange: [START_VALUE, END_VALUE], + outputRange: ["0%", "100%"], + }); + + return ( + + {children} + + ); +}; const styles = StyleSheet.create({ animationStyle: { diff --git a/src/animations/Raw.tsx b/src/animations/Raw.tsx index 4d63558..f3e3c01 100644 --- a/src/animations/Raw.tsx +++ b/src/animations/Raw.tsx @@ -1,7 +1,4 @@ import React from "react"; import { ViewProps } from "react-native"; -import { Provider } from "./context"; -export const Raw: React.FC = ({ children, ...props }) => ( - {children} -); +export const Raw: React.FC = ({ children }) => <>{children}; diff --git a/src/animations/Shine.tsx b/src/animations/Shine.tsx index eee63fd..e0ace36 100644 --- a/src/animations/Shine.tsx +++ b/src/animations/Shine.tsx @@ -1,56 +1,49 @@ -import React from "react"; +import React, { useEffect, useRef } from "react"; import { Animated, StyleSheet, ViewProps } from "react-native"; -import { Provider } from "./context"; +import { AnimationContext } from "./context"; const START_VALUE = 0; const END_VALUE = 100; const isInteraction = false; -export interface IShine extends ViewProps { +export interface ShineProps extends ViewProps { /* Animation duration, default is 750 */ duration?: number; } -export class Shine extends React.Component { - private animation: Animated.Value; - constructor(props: IShine) { - super(props); +export const Shine: React.FC = ({ duration, children, style }) => { + const animation = useRef(new Animated.Value(START_VALUE)); - this.animation = new Animated.Value(0); - } + const start = () => { + animation.current.setValue(START_VALUE); - public componentDidMount() { - this.start(); - } - - public render() { - const { children, style } = this.props; - - const left = this.animation.interpolate({ - inputRange: [START_VALUE, END_VALUE], - outputRange: ["0%", "100%"], - }); - - return ( - {children} - ); - } - - private start() { - this.animation.setValue(START_VALUE); - - Animated.timing(this.animation, { - duration: this.props.duration || 750, + Animated.timing(animation.current, { + duration: duration || 750, isInteraction, toValue: END_VALUE, useNativeDriver: false, }).start((e) => { if (e.finished) { - this.start(); + start(); } }); - } -} + }; + + useEffect(() => { + start(); + }, []); + + const left = animation.current.interpolate({ + inputRange: [START_VALUE, END_VALUE], + outputRange: ["0%", "100%"], + }); + + return ( + + {children} + + ); +}; const styles = StyleSheet.create({ shine: { diff --git a/src/animations/ShineOverlay.tsx b/src/animations/ShineOverlay.tsx index d5738a9..14b8452 100644 --- a/src/animations/ShineOverlay.tsx +++ b/src/animations/ShineOverlay.tsx @@ -1,58 +1,52 @@ -import React from "react"; +import React, { useEffect, useRef } from "react"; import { Animated, StyleSheet, View } from "react-native"; const START_VALUE = 0; const END_VALUE = 100; const isInteraction = false; -export interface IShine { +export interface ShineOverlayProps { /* Animation duration, default is 750 */ duration?: number; } -export class ShineOverlay extends React.Component { - private animation: Animated.Value; - constructor(props: IShine) { - super(props); +export const ShineOverlay: React.FC = ({ + duration, + children, +}) => { + const animation = useRef(new Animated.Value(START_VALUE)); - this.animation = new Animated.Value(0); - } + const start = () => { + animation.current.setValue(START_VALUE); - public componentDidMount() { - this.start(); - } - - public render() { - const { children } = this.props; - - const left = this.animation.interpolate({ - inputRange: [START_VALUE, END_VALUE], - outputRange: ["0%", "100%"], - }); - - return ( - - {children} - - - ); - } - - private start() { - this.animation.setValue(START_VALUE); - - Animated.timing(this.animation, { - duration: this.props.duration || 750, + Animated.timing(animation.current, { + duration: duration || 750, isInteraction, toValue: END_VALUE, useNativeDriver: false, }).start((e) => { if (e.finished) { - this.start(); + start(); } }); - } -} + }; + + useEffect(() => { + start(); + }, []); + + const left = animation.current.interpolate({ + inputRange: [START_VALUE, END_VALUE], + outputRange: ["0%", "100%"], + }); + + return ( + + {children} + + + ); +}; const styles = StyleSheet.create({ shine: { diff --git a/src/animations/context.ts b/src/animations/context.ts index ea7939c..0a20ed3 100644 --- a/src/animations/context.ts +++ b/src/animations/context.ts @@ -1,3 +1,4 @@ -import React from "react"; +import React, { useContext } from "react"; +export const AnimationContext = React.createContext({}); -export const { Consumer, Provider } = React.createContext({}); +export const useAnimation = () => useContext(AnimationContext); diff --git a/src/index.tsx b/src/index.tsx index 73ba40f..16856fd 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,4 +1,4 @@ -export { AnimationConsumer } from "./animations/AnimationConsumer"; + export { Placeholder } from "./Placeholder"; export { PlaceholderLine } from "./PlaceholderLine"; export { PlaceholderMedia } from "./PlaceholderMedia"; diff --git a/src/tokens.tsx b/src/tokens.tsx index cb12c25..057c88a 100644 --- a/src/tokens.tsx +++ b/src/tokens.tsx @@ -8,10 +8,10 @@ interface ISizes { } export const COLORS: IColors = { - primary: "#efefef" + primary: "#efefef", }; export const SIZES: ISizes = { normal: 12, - xxl: 40 + xxl: 40, };