How do I alternate two Images in a react native component - react-native

I'm trying to animate an icon in React native by simply switching the image every 500ms. My code looks like this:
export default class FlashingIcon extends Component {
constructor(props) {
super(props);
this.state = {
on: true,
};
setInterval(() => {
this.setState(previousState => {
return {
on: !previousState.on,
};
});
}, 500);
}
render() {
let sprite = this.state.on
? require('../onIcon.png')
: require('../offIcon.png');
return (
<Image
source={sprite}
style={{width:16, height:20}}
/>
);
}
}
The code is basically copy-and-pasted from:
https://facebook.github.io/react-native/docs/state.html and
https://facebook.github.io/react-native/docs/images.html
Each image shows up if I just copy the require into the <Image>. I can also verify that if I instead render a <Text> element outputting this.state.on, it shows the correct value alternating.
I can't for the life of me work out what I've done wrong.

Add key to Image It will help in re-rendering image once state changed.
<Image
key={this.state.on}
source={sprite}
style={{width:16, height:20}}
/>

Related

Binding on finish callback after Animated.Value has started animating?

I'm in the midst of learning React Native's Animated library, but have stumbled across a problem for which I'm sure there must be a solution, but can't seem to find it.
Context:
I have a component which creates a new Animated.Value and starts animating it. I then want to pass that single value to sub components so they can all animate in their own away against this canonical value. But I also want some of these sub components to handle the on finished event.
Currently:
The only way the docs explain handling the animation on finished event is by passing a callback function to .start(). But the parent component which starts the animation doesn't know how the multiple sub components intend to handle it.
Question:
Is there anyway to bind this callback after the animation has started? Something like this.props.animatingValue.addOnFinished(...) would be nice :)
Please let me know if I'm misunderstanding something fundamental. Is it a bad idea to pass Animated.Value instances as props? If so, what's a better way to approach this problem?
Thanks!
EDIT (Request for code example):
I'm just making this up as I go, so please forgive any syntax errors, but this should demonstrate what I'm trying to do:
class Parent extends React.Component {
componentDidMount() {
this.setState({
animatedValue: new Animated.Value(0)
}, () => {
Animated.timing(this.state.animatedValue, {
toValue: 1,
duration: 1000
}).start()
})
}
render() {
return this.state.animatedValue ? (
<View>
<ChildOne animatedValue={this.state.animatedValue} />
<ChildTwo animatedValue={this.state.animatedValue} />
<ChildThree animatedValue={this.state.animatedValue} />
</View>
) : null
}
}
class ChildOne extends React.Component {
componentDidMount() {
// What I'd like to do...
this.props.animatedValue.onFinished(() => { /* ... something ... */ }
}
render() { /* interpolate some style against the animation, not important */ }
}
// same for ChildTwo and ChildThree ...
Something like this would work. Use this approach in all your children.
class Child extends React.Component {
componentDidMount() {
this.props.addAnimationEndedListener(this.onAnimationEnded);
}
onAnimationEnded = (finished)=>{
/*The animation has Ended. Do what you want in the child here*/
};
}
And then in parent,
class Parent extends React.Component {
componentDidMount() {
this.setState({
animatedValue: new Animated.Value(0)
}, () => {
Animated.timing(this.state.animatedValue, {
toValue: 1,
duration: 1000
}).start(this.onAnimationEnded)
});
this.animationListeners = [];
}
addAnimationEndedListener = (listener)=>{
this.animationListeners.push(listener);
}
onAnimationEnded = ({finished})=>{
this.animationListeners.forEach(notifyListener=>{
notifyListener(finished);
});
}
render() {
return this.state.animatedValue ? (
<View>
<ChildOne animatedValue={this.state.animatedValue} addAnimationEndedListener={this.addAnimationEndedListener} />
<ChildTwo animatedValue={this.state.animatedValue} addAnimationEndedListener={this.addAnimationEndedListener} />
<ChildThree animatedValue={this.state.animatedValue} addAnimationEndedListener={this.addAnimationEndedListener} />
</View>
) : null
}
}

How to detect if a view is visible in a viewport or window in react native?

I want a similar feature just like react visibility sensor, but in react-native. I have a flat list with multiple items(each having different height). I want to detect whether a particular item(lets say 5th item) comes inside the view port and when it goes out
You can use onViewableItemsChanged to check which viewableItems are on the screen.
Here's a dummy class example:
class Demo extends Component {
constructor() {
super();
this.viewabilityConfig = {
viewAreaCoveragePercentThreshold: 95
}
}
onViewableItemsChanged = ({ viewableItems }) => {
// viewableItems will show you what items are in view
}
render() {
<FlatList
...
onViewableItemsChanged={this.onViewableItemsChanged}
viewabilityConfig={this.viewabilityConfig}
/>
}
}
You'll need to modify viewAreaCoveragePercentThreshold accordingly.
You can use Viewport from '#skele/components' like this:
0. Install Skele Components:
yarn add #skele/components or npm install #skele/components
1. Wrap your scrollable view with Viewport.Tracker
import { Viewport } from '#skele/components'
...
render() {
return (
<Viewport.Tracker>
<ScrollView scrollEventThrottle={16}>
{ this.props.children }
</ScrollView>
</Viewport.Tracker>
);
}
2. Make its child components Viewport.Aware
import { Image } from 'react-native'
import { Viewport } from '#skele/components'
...
const ViewportAwareImage = Viewport.Aware(Image)
...
render() {
return (
<ViewportAwareImage
source={{ uri: 'https://facebook.github.io/react-native/img/header_logo.png' }}
onViewportEnter={() => console.log('Entered!')}
onViewportLeave={() => console.log('Left!')}
/>
);
}
for more info visite this link

React Native Change Page after 5 seconds

I'm newbie in react native and I don't know how to change page after 5 seconds.
I create an android.index.js file that will navigate to LandingPage.js. What I want to do is, when the LandingPage being loaded, it will wait for 5 seconds and then redirect / navigate to another page.
index.android.js
export default class DefaultProject extends Component {
render() {
return (
<Navigator
renderScene={(route, navigator) =>
<LandingPage/>
}
/>
)
LandingPage.js
export default class LandingPage extends Component {
render() {
return (
<Image source={require('./images/event3.jpeg')}
style={styles.container} />
//How to redirect to another page from here after 5 secs?
);
}
}
You can use a simple setTimeout, as you would in a standard JS setup:
export default class LandingPage extends Component {
componentDidMount(){
// Start counting when the page is loaded
this.timeoutHandle = setTimeout(()=>{
// Add your logic for the transition
}, 5000);
}
componentWillUnmount(){
clearTimeout(this.timeoutHandle); // This is just necessary in the case that the screen is closed before the timeout fires, otherwise it would cause a memory leak that would trigger the transition regardless, breaking the user experience.
}
render() {
return (
<Image source={require('./images/event3.jpeg')}
style={styles.container} />
//How to redirect to another page from here after 5 secs?
);
}
}
I'm using lodash for this:
export default class Splash extends React.Component {
constructor(props) {
super(props);
}
async componentWillMount() {
_.delay(() => this.props.navigator.replace({ component: 'login' }), 1000);
}
render() {
return (
...
);
}
}
This will only work if you have a Navigator set up. Check this article:
React Native Navigator — Navigating Like A Pro in React Native

React Native Android lag during animation

I experience the lag during transition animation when Navigator goes to below scene ShiftEdit. Animation starts immediately but it stops for a millisecond. InteractionManager is used to postpone rendering of four picker components. Every picker component has list of items that is built from an array. There is lots of items. Is it possible that this is calculated even when picker component isn't rendered yet in ShiftEdit and this is the reason of the lag? Could you help me please?
'use strict'
import React, {View, Text, StyleSheet, InteractionManager, TouchableOpacity} from 'react-native';
import { connect } from 'react-redux';
import Spinner from 'react-native-spinkit';
import StyleCommon from '../styles';
import TimePicker from '../components/time-picker';
import ColorPicker from '../components/color-picker';
import LabelPicker from '../components/label-picker';
class ShiftEdit extends React.Component {
constructor(props) {
super(props);
this.state = {
isReady: false,
shiftId: '',
startHour: '',
endHour: '',
color: '',
}
}
componentDidMount() {
InteractionManager.runAfterInteractions(() => {
this.setState({isReady: true});
});
}
onChangeItem = (label, val) => {
let data = {};
data[label] = val;
this.setState(data);
}
renderPlaceholder() {
return (
<View style={styles.container}>
<Text>Loading...</Text>
</View>
)
}
render() {
if (!this.state.isReady) {
return this.renderPlaceholder();
}
return (
<View style={{flex:1, flexDirection: 'column'}}>
<TimePicker label={'Start hour'} value={this.state.startHour} onChange={this.onChangeItem.bind(this, 'startHour')} />
<TimePicker label={'End hour'} value={this.state.endHour} onChange={this.onChangeItem.bind(this, 'endHour')} />
<ColorPicker label={'Color'} value={this.state.color} onChange={this.onChangeItem.bind(this, 'color')} />
<LabelPicker label={'Shift ID'} value={this.state.shiftId} onChange={this.onChangeItem.bind(this, 'shiftId')} />
</View>
)
}
};
I tried to control animation registration as Chris suggested but it still the same:
onPress = () => {
let handle = InteractionManager.createInteractionHandle();
this.props.navigator.push({component: 'shiftedit'});
InteractionManager.clearInteractionHandle(handle);
}
Actually this is the only solution that works for me now:
componentDidMount() {
// InteractionManager.runAfterInteractions(() => {
// this.setState({isReady: true});
// })
setTimeout(() => {
this.setState({isReady: true});
}, 75);
}
but I'd rather use InteractionManager...
Here's a wild guess as I've no experience with InteractionManager directly. But after looking over the Interaction Manager Docs I noticed that there's a way to register animations. So, my guess is that the Navigator's animations haven't been properly registered. So maybe try something like this...
var handle = InteractionManager.createInteractionHandle();
// run your navigator.push() here... (`runAfterInteractions` tasks are queued)
// later, on animation completion:
InteractionManager.clearInteractionHandle(handle);
// queued tasks run if all handles were cleared
Hope that helps!
Also, keep in mind that if you're running the React Native Debugger while testing your app, React Native animations will appear jittery on Android.
That's been my experience.
https://github.com/jhen0409/react-native-debugger

Show default element while loading Image

I have a component representing an user avatar that loads an image from my API.
I want it to display a default avatar (not another image) while the avatar is loading.
constructor() {
super();
this.state = {
loaded: false,
};
}
render() {
if (!this.props.uri || !this.state.loaded) {
return (
<DefaultAvatar />
);
}
return <Image onLoad={this.onLoad.bind(this)} uri={this.props.uri} />;
}
onLoad() {
this.setState({loaded: true});
}
The problem I have is that with this current code, the Image will never be rendered, so the state will never change. I'm unable to find a solution that would satisfy React principles and my requirements (no ghost components to load the image before displaying it).
class LazyImage extends React.Component{
constructor () {
super(this.props)
this.state = {loaded: false}
}
handleLoad () {
this.setState({loaded:true})
}
componentDidMount () {
this.img = new Image()
this.img.onload = this.handleLoad.bind(this)
this.img.src = this.props.src
}
render () {
return this.state.loaded?<img src={this.props.src}/>:<div>Loading...</div>
}
}
You create a native Image element and wait for it to load. Then you render the image with react. The browser is smart and fetches it from the cache this time. Instant render!
See http://jsfiddle.net/4hq3y4ra/3/ for a demo.
There are several ways this can be achieved, however to keep things simple, you can use a literal condition to toggle default avatar and the actual image.
constructor() {
super();
this.state = {
loaded: false,
};
}
onLoad(dataUri) {
if(dataUri !== undefined){
this.setState({loaded: true});
}
},
render() {
return (
<Image onLoad={this.onLoad} uri={this.state.loaded ? this.props.uri : 'default-avatar'} />
);
}
Image.prefetch will allow me to do what I want, thanks to everyone.