Skip to content

Instantly share code, notes, and snippets.

@pangolingo
Created December 27, 2017 16:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pangolingo/15c26a8d6d015cb858c1924f72a4e719 to your computer and use it in GitHub Desktop.
Save pangolingo/15c26a8d6d015cb858c1924f72a4e719 to your computer and use it in GitHub Desktop.
A React Native higher-order component that converts an animated value to a number
// @flow
import * as React from 'react';
import { Animated } from 'react-native';
type HOCProps = {
/**
* The distance that a ScrollView has been scrolled
*/
[key: string]: Animated.Value
}
type State = {
/**
* The ScrollView scroll distance as an number instead of an Animated.Value
*/
[key: string]: number,
}
/**
* This higher order component takes a component with an Animated.Value prop.
* Let's call it 'scrollDistance'. It returns a new component that converts
* the 'scrollDistance' animated value to an number using a listener.
* This new component then renders the original component,
* passing in the number 'scrollDistanceNumber'.
* @param {React.ComponentType<Props>} WrappedComponent - the original component that will
* receive the animated value as an number
* @param {string} propName - the name of the Animated Value property that should be converted into
* a usable number.
* @returns {React.ComponentType<Props>} - a new component with the original one inside
*/
export default function AnimatedPropAsNumberHOC<Props: HOCProps>(
WrappedComponent: React.ComponentType<Props>,
propName: string,
): React.ComponentType<Props> {
// return the original component wrapped in a new component
return class extends React.Component<Props, State> {
/**
* @param {object} props - React component props
*/
constructor(props: Props) {
super(props);
this.propNameNumber = `${propName}Number`;
this.state = {
[this.propNameNumber]: 0,
};
}
/**
* When the component mounts, add a listener to the animated prop
*/
componentWillMount() {
if (this.props[propName]) {
this.props[propName].addListener(this.animatedListener);
}
}
/**
* When the component unmounts, destroy the listener
*/
componentWillUnmount() {
if (this.props[propName]) {
// cleanup to avoid memory leaks
this.props[propName].removeAllListeners();
}
}
/**
* The name of the new prop (propName + 'Number')
*/
propNameNumber: ?string = undefined;
/**
* listen to the animated value, and save the position as an Number
*/
animatedListener = ({ value }: { value: number }) => {
if (!this.propNameNumber) return;
this.setState({ [this.propNameNumber]: value });
}
/**
* this is an HOC: render the original component, passing in the new number prop and
* all the original props
* @returns {React.Node} - the original component
*/
render(): React.Node {
if (!this.propNameNumber) return null;
const newProps = { [this.propNameNumber]: this.state[this.propNameNumber] };
return <WrappedComponent {...newProps} {...this.props} />;
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment