react-native: disable animations - animation

We are using Animated and react-native-animatable quite heavily and starting to notice slowness on some old devices. All animations set useNativeDriver which makes us believe that we may have a few too many animations.
Is there a way to overwrite the Animated prototype to completely disable animations? I looked into this and it didn't seem simple.
Another option I'm considering is to leave my fade animations in but set the initial value in the constructor to the final value. This approach definitely doesn't show any animations but would it also bypass the animation in the native bridge as the value isn't changing?
class Item extends Component {
constructor(props) {
super(props);
this.state = {
opacity: 1 // Notice how this is set to 1
}
}
componentDidMount() {
setTimeout(() => {
this.setState({opacity: 1})
}, 1000)
}
render() {
return (
<Animatable.View style={{opacity}} easing='ease-in' transition='opacity' duration={500} useNativeDriver={true} />
)
}
}

Just create a wrapping component for it and use that instead of Animated.View
export default const AnimatedViewWrapper = (props) => {
if (/* slow device check */) {
return React.createElement(View, props)
} else {
return React.createElement(Animated.View, props)
}
}
You might need to filter the props you receive because View does not have many of the props that Animated.View has. You can get them through View.propTypes. You might need to do this only if __DEV__ is true as propTypes are stripped out in production builds

Related

pixijs: create custom event in sprite and listen in another sprite

I have two custom Sprites (in my case on is Sprite, another is Container, but this doesn't matter). I need to emit event in one sprite and listen to it in another sprite (MyDragableElement). In Application I have many MyDraableElements: PIXI.Sprite.
The idea is that second sprite will be able to manipulate on MyDragableElement, but it should know which element is selected. I decided that events can help here, but how this should be written in Typescript?
export class MyDragableElement extends PIXI.Sprite {
constructor(
super();
this
.on('pointerdown', this.onDragStart)
.on('pointerup', this.onDragEnd)
.on('pointerupoutside', this.onDragEnd)
.on('pointermove', this.onDragMove);
}
private onDragStart(event) {
this.emit('selectedElementChanged', this);
}
}
So as you see this is some sprite and on drag start, it should emit global event.
Here is another Sprite, and it should listen to this event.
export class Toolbar extends PIXI.Container {
selectedGameObject: any;
constructor()
{
super();
this.addListener("selectedElementChanged",
(e:any) => {
// this.selectedGameObject = need to select this element
console.log(e);
});
}
}
}
Above is my idea, but it worn't work. No experience in Emitting events.
I just learned this topic and can be wrong, but...
if you have some sprite
let mySprite = new Sprite(someTexture);
... you can write somewhere in you code the next:
mySprite.emit('customEvent', {myData: 'somedata'});
And you also need to set event handler for this customEvent on your sprite:
mySprite.on('customEvent', onCustomEvent);
let onCustomEvent = (event) => {
console.log(event);
};

Complex navigation in React Native using react-navigation and Redux

I have the following navigation structure in my React Native app:
StackNavigator configured with 3 routes:
Splash screen (React Component)
StackNavigator for my login flow
DrawerNavigator for my core app screens.
The DrawerNavigator has some dynamic multiple routes, but also one static route which is another StackNavigator.
Everything seems to be working as expected:
The store is being updated accordingly.
Navigation between screen works.
Go back between screen works when configured within each component, with the following command:
this.props.navigation.goBack();
My question is - is there a way for me to handle back button on Android globally? Currently when I click on the back button, nothing happens (due to the fact I'm using Redux). Should I handle the back button in each component or is there a way of doing it using Redux?
A bit late, but there is a way to handle this with redux. In your index.js file where you create your store you can make export a class and add a componentWillMount call to handle dispatching a call to your redux actions. Just remember to import the actions you need above.
const store = configureStore();
export default class Index extends Component {
componentWillMount = () => {
BackHandler.addEventListener('hardwareBackPress', () => {
const { nav: { routes } } = store.getState();
const currentRouteName = routes[routes.length-1].routeName;
if (currentRouteName === 'EditCoupleProfile') {
store.dispatch(editCoupleActions.navigateBack())
} else if ( currentRouteName === 'EditInterests' ) {
store.dispatch(interestsActions.navigateBack())
} else {
store.dispatch(popFromStack());
}
return true;
})
};
componentWillUnmount = () => {
BackHandler.removeEventListener('hardwareBackPress');
};
render() {
return (
<Provider store={store}>
<AppWithNavigation />
</Provider>
);
}
}

React native tabview configure sliding speed

I'm using react native tab view https://github.com/react-native-community/react-native-tab-view to have something like a carousel. It seems to work fine, but the sliding transition is too fast for me. How can I configure it? Docs say that there's a configureTransition callback which should return the transition configuration, but doesn't say what's that configuration and how should it look like:
configureTransition - optional callback which returns a configuration for
the transition, return null to disable animation
Please, help me to find out how to configure transition speed.
Transition spec is defined in this file.
import { Animated } from 'react-native';
_configureTransition = () => {
return {
timing: Animated.spring,
tension: 300,
friction: 100,
};
}
render() {
return (
<TabViewAnimated
....
configureTransition={this._configureTransition}
/>
);
}

Which component should control the loading state of a lower component?

Let's say I have these components:
Translator
TranslationList
Translator determines translation context, has translate function.
TranslationList must show these "visual states": loading, result list, no results.
The Translator moves around the page (one instance): on focusing an input, it moves "below" it and gives a dropdown with suggestion.
So each time it moves, it has to:
Show that it's loading translations
Show translation list or no results message.
So my question is:
Which component should control the "loading" visual state?
If the Translator component controls it, it has to pass loading=true translations=[] as props to Translation list. Then later it has to rerender it again with new props loading=false translations=[...]. This seems a bit counter-intuitive, because loading feels like the state of the TranslationList component.
If we the TranslationList component has loading state, then it also has to have a way to translate things, meaning that I have to pass translate function as prop. I would then hold translations and loading as state. This all gets a bit messy, since it must now also receive string to translate, context.
I also don't want to have separate components for loading message, no results message. I'd rather keep these inside the TranslationList, because these 3 share that same wrapper <div class="list-group"></div>
Perhaps there should be one more Component in between these two components, responsible only for fetching translation data?
Translator component should control the loading state of a lower component list component. hold the loading and translating logic but with help by wrapping it in a high order component where you should put most of the logic. link for HOC https://www.youtube.com/watch?v=ymJOm5jY1tQ.
const translateSelected = wrappedComponent =>
//return Translator component
class extends React.Component {
state = {translatedText: [], loading:true}
componentDidMount(){
fetch("text to translate")
.then(transText => this.setState({translatedText: transText, loading: false}))
}
render() {
const {translatedText} = this.state
return <WrappedComponent {..this.props} {...translatedText}
}
}
const Translator_HOC = translateSelected(Translator);
You could introduce a Higher Order Component to control the switching of the loading state and the TranslationList. That way you separate the loading display away from your TranslationList as being it's concern. This also allows you to use the HOC in other areas.
The Translator can act as "container" component which does the data fetching/passing.
For example:
// The Loadable HOC
function Loadable(WrappedComponent) {
return function LoadableComponent({ loaded, ...otherProps }) {
return loaded
? <WrappedComponent {...otherProps} />
: <div>Loading...</div>
}
}
// Translation list doesn't need to know about "loaded" prop
function TranslationList({ translations }) {
return (
<ul>
{
translations.map((translation, index) =>
<li key={index}>{translation}</li>
)
}
</ul>
)
}
// We create our new composed component here.
const LoadableTranslationList = Loadable(TranslationList)
class Translator extends React.Component {
state = {
loaded: false,
translations: []
}
componentDidMount() {
// Let's simulate a data fetch, typically you are going to access
// a prop like this.props.textToTranslate and then pass that to
// an API or redux action to fetch the respective translations.
setTimeout(() => {
this.setState({
loaded: true,
translations: [ 'Bonjour', 'Goddag', 'Hola' ]
});
}, 2000);
}
render() {
const { loaded, translations } = this.state;
return (
<div>
<h3>Translations for "{this.props.textToTranslate}"</h3>
<LoadableTranslationList loaded={loaded} translations={translations} />
</div>
)
}
}
ReactDOM.render(<Translate textToTranslate="Hello" />)
Running example here: http://www.webpackbin.com/NyQnWe54W

React Native: Triggering Animation on hide

I have an element controlling the rendering of a child element. (A TouchableHighlight that sets some state in its onPress.) In the child element's componentDidMount method I construct an Animated.spring and start it. This works for entry, but I need to do the same animation in reverse to exit (it's like a drawer). componentWillUnmount executes too quickly for Animated.spring to even start working.
How would I handle animating the child's exit?
I have implemented a FadeInOut component that will animate a component in or out when its isVisible property changes. I made it because I wanted to avoid explicitly handling the visibility state in the components that should enter/exit with an animation.
<FadeInOut isVisible={this.state.someBooleanProperty} style={styles.someStyle}>
<Text>Something...</Text>
</FadeInOut>
This implementation uses a delayed fade, because I use it for showing progress indicator, but you can change it to use any animation you want, or generalise it to accept the animation parameters as props:
'use strict';
import React from 'react-native';
const {
View,
Animated,
PropTypes
} = React;
export default React.createClass({
displayName: 'FadeInOut',
propTypes: {
isVisible: PropTypes.bool.isRequired,
children: PropTypes.node.isRequired,
style: View.propTypes.style
},
getInitialState() {
return {
view: this.props.children,
opacity: new Animated.Value(this.props.isVisible ? 1 : 0)
};
},
componentWillReceiveProps(nextProps) {
const isVisible = this.props.isVisible;
const shouldBeVisible = nextProps.isVisible;
if (isVisible && !shouldBeVisible) {
Animated.timing(this.state.opacity, {
toValue: 0,
delay: 500,
duration: 200
}).start(this.removeView);
}
if (!isVisible && shouldBeVisible) {
this.insertView();
Animated.timing(this.state.opacity, {
toValue: 1,
delay: 500,
duration: 200
}).start();
}
},
insertView() {
this.setState({
view: this.props.children
});
},
removeView() {
this.setState({
view: null
});
},
render() {
return (
<Animated.View
pointerEvents={this.props.isVisible ? 'auto' : 'none'}
style={[this.props.style, {opacity: this.state.opacity}]}>
{this.state.view}
</Animated.View>
);
}
});
I think you have the animation ownership inverted. If you move your animation logic to the parent that is opening and closing the child, the problem becomes much simpler. Rather than beginning the animation on componentDidMount, do it on the click of your TouchableHighlight in addition to, but independent of, whatever prop manipulations on the child you need to do.
Then when the user clicks to close, you can simply reverse the animation as per normal and you don't really even need to unload it. Also this would allow you to have a reusable drawer (the thing that slides up and down) and it's abstracted away from the content within it. So you can have a single drawer mechanism supporting multiple different types of content.

Resources