How to make a React Native animation happen again? - reactjs

Currently, my React Native animation only happens one time then never again. I need it to happen every time one of my props for that component changes. I have the data display changing when the new prop data comes in but it only animates the first time. Is there a way for me to for the animation to happen again every time that props changes/the component updates?
Here is what I have so far:
import React from 'react';
import {Animated, Easing, StyleSheet, Text, View} from 'react-native';
//Animation
class FadeInView extends React.Component {
state = {
yAnimation: new Animated.Value(21),
}
componentDidMount() {
Animated.timing(
this.state.yAnimation,
{
//easing: Easing.bounce,
toValue: 0,
duration: 150,
}
).start();
}
render() {
let { yAnimation } = this.state;
return (
<Animated.View
style={{
...this.props.style,
transform: [{translateY: this.state.yAnimation}],
}}
>
{this.props.children}
</Animated.View>
);
}
}
//Price Component
export default class Price extends React.Component {
constructor(props) {
super(props);
this.animateNow = false;
}
shouldComponentUpdate(nextProps, nextState) {
if (this.props.price !== nextProps.price) {
console.log('true');
return true;
} else {
return false;
}
}
componentWillUpdate() {
if (this.props.price != this.localPrice) {
this.animateNow = true;
}
}
componentDidUpdate() {
this.localPrice = this.props.price;
this.animateNow = false;
console.log(this.props.price);
}
render() {
if (this.animateNow) {
return (
<FadeInView>
<Text style={styles.price}>{this.props.price}</Text>
</FadeInView>
);
} else {
return (
<View>
<Text style={styles.price}>{this.props.price}</Text>
</View>
);
}
}
}
const styles = StyleSheet.create({
price: {
fontFamily: 'Avenir',
fontSize: 21,
color: '#606060',
textAlign: 'right',
marginRight: 20,
backgroundColor: 'transparent'
}
});

If you want to animate again when receive props, you should call that again inside componentWillReceiveProps():
playAnimation() {
Animated.timing(
this.state.yAnimation,
{
toValue: 0,
duration: 150,
}
).start();
}
componentWillReceiveProps(next) {
if (next.props.changed) {
this.playAnimation();
}
}

Related

react native function return value not showing

i am using react native code but some how code not working. please let me check how i can fix. i am getting value from cache and trying to return & show value.
i tried lot some how code not working if someone has any idea please let me know
import React, { memo } from 'react';
import { Text, View, StyleSheet, AsyncStorage } from 'react-native';
import { theme } from "../core/theme";
class Dashdata extends React.Component{
constructor(){
super();
this.getDataName = this.getDataName.bind(this);
this.state = {
displayname: ''
};
}
getDataName = () => {
const displayname = '';
console.log('getting value from cachedd');
const loginName = AsyncStorage.getItem('#kidssafety:displayname')
.then((result)=>{
console.log(result);
return (
<Text>{result}</Text>
)
});
}
render(){
return(
<View>
<Text style={styles.header}>Welcome Data {this.getDataName()}</Text>
</View>
)
}
}
const styles = StyleSheet.create({
header: {
fontSize: 22,
color: theme.colors.primary,
fontWeight: "bold",
paddingVertical: 14,
flex: 1,
marginTop: 100,
width: '100%',
textAlign: 'left'
}
});
export default memo(Dashdata);
AsyncStorage returns a promise. So you have to wait until it resolves.
Use Async/await to fix your problem.
getDataName = async () => {
const loginName = await AsyncStorage.getItem('#kidssafety:displayname')
this.setState({
displayname: loginName
})
}
Now you can display your values inside render
<Text style={styles.header}>Welcome Data {this.state.displayname}</Text>
Important
Since you are using as getDataName as an arrow function, you don't have to bind it as
this.getDataName = this.getDataName.bind(this)
Hope this helps you. Feel free for doubts.
AsyncStorage.getItem returns a promise and by the time it resolves and returns a value, your render would have moved to the next line. Ideally you should store the result to the state and use it when ready. Then your component will look like.
import React, { memo } from 'react';
import { Text, View, StyleSheet, AsyncStorage } from 'react-native';
import { theme } from "../core/theme";
class Dashdata extends React.Component{
constructor(){
super();
this.getDataName = this.getDataName.bind(this);
this.state = {
displayname: '',
result: '' // add result here
};
}
getDataName = () => {
const displayname = '';
console.log('getting value from cachedd');
const loginName = AsyncStorage.getItem('#kidssafety:displayname')
.then((result)=>{
console.log(result);
this.setState({result}) // set result to state
});
}
render(){
const { result } = this.state
return(
<View>
{!!result && (<Text style={styles.header}>Welcome Data {result})</Text>}
</View>
)
}
}
const styles = StyleSheet.create({
header: {
fontSize: 22,
color: theme.colors.primary,
fontWeight: "bold",
paddingVertical: 14,
flex: 1,
marginTop: 100,
width: '100%',
textAlign: 'left'
}
});
export default memo(Dashdata);

Changing the style of "toggled" elements in a list, react-native

I'm having trouble changing the style of only one element in a list.
Below is my Main class, as well as StationDetails class, which is a component I've created to render the list elements one by one.
There is one line (Line 31) in the StationDetails I cant seem to figure out the problem with. I want to style the component based on whether or not the elements' ID is included in the activeStations list.
Here is the line:
style={activeStations.includes(stations.id) ? pressedStyle : buttonStyle}
Here is my Main class
import React, { Component } from "react"
import axios from "axios"
import { Text, View, ScrollView } from "react-native"
import StationDetails from "./StationDetails"
class Main extends Component {
constructor(props) {
super(props)
this.state = { stations: [], pressStatus: false, activeStations: [] }
this.handleClick = this.handleClick.bind(this)
}
componentWillMount() {
axios
.get("https://api.citybik.es/v2/networks/trondheim-bysykkel")
.then(response =>
this.setState({ stations: response.data.network.stations })
)
}
handleClick() {
this.setState({ pressStatus: !this.state.pressStatus })
}
renderStations() {
return this.state.stations.map(stations => (
<StationDetails
activeStations={this.state.activeStations}
handleClick={this.handleClick}
pressStatus={this.state.pressStatus}
key={stations.id}
stations={stations}
>
{stations.name}
</StationDetails>
))
}
render() {
return (
<ScrollView style={{ flex: 1, marginTop: 20 }}>
{this.renderStations()}
</ScrollView>
)
}
}
export default Main
And here is my StationDetails component.
import React from "react"
import { Text, View } from "react-native"
import Card from "./felles/Card"
import CardSection from "./felles/CardSection"
import Button from "./felles/Button"
const StationDetails = ({
stations,
handleClick,
pressStatus,
activeStations
}) => {
const {
headerTextStyle,
leftPartStyle,
rightPartStyle,
pressedStyle,
buttonStyle
} = styles
return (
<Card style={{ flex: 1, flexDirection: "row" }}>
<CardSection style={leftPartStyle}>
<Text style={headerTextStyle}>
{stations.name}
</Text>
<Text>
Free bikes: {stations.free_bikes}
</Text>
</CardSection>
<CardSection style={rightPartStyle}>
<Button
onPress={() => {
if (!activeStations.includes(stations.id)) {
activeStations.push(stations.id)
} else {
activeStations.splice(activeStations.indexOf(stations.id), 1)
}
}}
style={
activeStations.includes(stations.id) ? pressedStyle : buttonStyle
}
>
Abonner
</Button>
</CardSection>
</Card>
)
}
const styles = {
textStyle: {
fontSize: 14
},
leftPartStyle: {
flex: 3,
flexDirection: "column",
justifyContent: "space-between"
},
rightPartStyle: {
flex: 1
},
pressedStyle: {
backgroundColor: "green"
},
headerTextStyle: {
fontSize: 18
},
thumbnailStyle: {
height: 50,
width: 50
},
buttonStyle: {
backgroundColor: "#fff"
}
}
export default StationDetails
You are trying to set state of Main.activeStations from StationDetails which is not advisable. Few things to keep in mind
Main's activeStations is in it's local component level state.
You shouldn't be trying to mutate that from a child component.
Since you assign mutated activeStations state to Main.activeStations from StationDetails, ReactNative (RN) doesn't find a difference in state in it's reconciliation process so does not re-render StationDetails.
We need RN to re-render StationDetails so that it will show the correct style for the buttons etc.
Documentation on setState
This is how I would do it
Let Main render StationDetails
Get a callback from StationDetails on which station was selected
Let Main take care of mutating it's own internal state (activeStations)
By doing it this way,
StationDetails is responsible only for rendering a list of stations given the props and nothing else. It's a dumb component that renders a list.
Main is responsible for handling it's own internal state
Heres the result :
Main.js class
import React, { Component } from 'react';
import axios from 'axios';
import { ScrollView } from 'react-native';
import StationDetails from './StationDetails';
export default class App extends Component {
constructor(props) {
super(props);
this.onSelectStation = this.onSelectStation.bind(this);
this.state = {
stations: [],
pressStatus: false,
activeStations: []
};
}
componentWillMount() {
axios.get('https://api.citybik.es/v2/networks/trondheim-bysykkel')
.then(response => this.setState({ stations: response.data.network.stations }));
}
onSelectStation(stationKey) {
const { activeStations } = this.state;
const activeStationsEdit = activeStations;
if (!activeStations.includes(stationKey)) {
activeStationsEdit.push(stationKey);
} else {
activeStationsEdit.splice(activeStations.indexOf(stationKey), 1);
}
this.setState({ activeStations: activeStationsEdit });
}
renderStations() {
return this.state.stations.map((stations) =>
<StationDetails
activeStations={this.state.activeStations}
pressStatus={this.state.pressStatus}
key={stations.id}
stations={stations}
stationId={stations.id}
onSelectStation={this.onSelectStation}
>
{stations.name}
</StationDetails>
);
}
render() {
return (
<ScrollView style={{ flex: 1, marginTop: 20 }}>
{this.renderStations()}
</ScrollView>
);
}
}
StationDetails class
import React from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
const StationDetails = ({ stations, activeStations, stationId, onSelectStation }) => {
const { headerTextStyle, leftPartStyle, container, pressedStyle, buttonStyle } = styles;
return (
<View style={container}>
<View style={leftPartStyle}>
<Text style={headerTextStyle}>
{stations.name}
</Text>
<Text>
Free bikes: {stations.free_bikes}
</Text>
</View>
<TouchableOpacity
onPress={() => onSelectStation(stationId)}
style={activeStations.includes(stations.id) ? pressedStyle : buttonStyle}
/>
</View>
);
}
const styles = {
container: {
flex: 1,
flexDirection: 'row',
marginBottom: 10
},
leftPartStyle: {
flex: 3,
flexDirection: 'column',
justifyContent: 'space-between'
},
pressedStyle: {
backgroundColor: 'green',
flex: 1,
},
headerTextStyle: {
fontSize: 18
},
buttonStyle: {
backgroundColor: 'skyblue',
flex: 1,
}
};
export default StationDetails;

Retrieve Prop Values from Imported Custom Component

So I have a custom component (TextButton) and I have packaged it inside another component (ContainedButton). I'm currently attempting to style ContainedButton. However, I want to access the value of a prop of TextButton (theme) and use the value when styling ContainedButton.
ContainedButton:
import React, { Component } from 'react';
import TextButton from './TextButton';
class ContainedButton extends Component {
render() {
const { style } = this.props;
return (
<TextButton {...this.props} style={[styles.containedButtonStyle, style]} />
);
}
}
const styles = {
containedButtonStyle: {
backgroundColor: (TextButton prop theme)
padding: 2,
borderWidth: 1,
borderRadius: 5
}
};
export default ContainedButton;
In the parentheses next to 'backgroundColor', I want to insert the value of the theme prop located in TextButton. How would I achieve something like this?
TextButton (in case it's needed):
import React, { Component } from 'react';
import { Text, TouchableOpacity } from 'react-native';
import PropTypes from 'prop-types';
class TextButton extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentWillMount() {}
componentWillReceiveProps(newProps) {
if (newProps.theme !== this.props.theme) {
this.determineTheme(newProps.theme);
}
if (newProps.size !== this.props.size) {
this.determineSize(newProps.size);
}
}
// set the theme
determineTheme = function (theme) {
if (theme === 'primary') {
return {
color: '#0098EE'
};
} else if (theme === 'secondary') {
return {
color: '#E70050'
};
} else if (theme === 'default') {
return {
color: '#E0E0E0'
};
}
return {
color: '#E0E0E0'
};
}
// set the size
determineSize = function (size) {
if (size === 'small') {
return {
fontSize: 16
};
} else if (size === 'medium') {
return {
fontSize: 22
};
} else if (size === 'large') {
return {
fontSize: 28
};
}
return {
fontSize: 22
};
}
render() {
const { onPress, children, theme, size, style } = this.props;
return (
<TouchableOpacity onPress={onPress} style={style}>
<Text style={[this.determineTheme(theme), this.determineSize(size)]}>{children}</Text>
</TouchableOpacity>
);
}
}
TextButton.propTypes = {
onPress: PropTypes.func,
title: PropTypes.string,
theme: PropTypes.string,
size: PropTypes.string
};
export default TextButton;
You can get the value of theme in the same way you get the value of style:
const { theme } = this.props;
Or combine them into a single statement:
const { style, theme } = this.props;

React native component life cycle: `ref` usage and `null` object

I have a parent component index.js
render() {
const { firstName, token } = this.props.user;
if (token && firstName) {
return (
<View style={{ flex: 1 }}>
<HomeRoot />
</View>
);
}
console.log('=== ELSE');
return (
<View style={{ flex: 1 }}>
<SplashScreen />
</View>
);
}
}
And a SplashScreen that shows while the user is not logged in:
// Methods imports.
import React from 'react';
import { View, Text, Image, TouchableOpacity, StyleSheet } from 'react-native';
import { connect } from 'react-redux';
import { Asset, AppLoading, Font, DangerZone } from 'expo';
import FadeInView from '../animations/FadeInView';
// Redux actions
import { signinUser } from '../../store/actions/actions';
const { Lottie } = DangerZone;
const styles = StyleSheet.create({
wrapper: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
// ...
});
function cacheImages(images) {
return images.map(image => {
if (typeof image === 'string') {
return Image.prefetch(image);
}
return Asset.fromModule(image).downloadAsync();
});
}
function cacheFonts(fonts) {
return fonts.map(font => Font.loadAsync(font));
}
class SplashScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
isReady: false
};
this.bgAnim = null;
}
setBgAnim(anim) {
// if (anim === null) {
// return;
// }
this.bgAnim = anim;
this.bgAnim.play();
}
async loadAssetsAsync() {
const imageAssets = cacheImages([
// ...
]);
const fontAssets = cacheFonts([{
'cabin-bold': CabinBold,
'league-spartan-bold': LeagueSpartanBold
}]);
await Promise.all([...imageAssets, ...fontAssets]);
}
render() {
if (!this.state.isReady) {
return (
<AppLoading
startAsync={this.loadAssetsAsync}
onFinish={() => this.setState({ isReady: true })}
/>
);
}
return (
<View style={styles.wrapper}>
<Lottie
ref={c => this.setBgAnim(c)}
resizeMode="cover"
style={{
position: 'absolute',
zIndex: 1,
left: 0,
top: 0,
width: '100%',
height: '100%',
}}
source={require('../../../assets/SPLASH_03.json')} // eslint-disable-line
/>
</View>
);t
}
}
export default connect(
null,
{ signinUser }
)(SplashScreen);
The signinUser calls facebookAuth and then store the fetched user profile and token in one unique dispatch.
At this point the index.js
token && firstName are true and the SplashScreen component should let his place to HomeRoot.
However it crashes on SplashScreen render method: ref={c => this.setBgAnim(c)}. If we remove this line, or check to discard c when c is null everything works as expected.
Why is c null at this stage in ref={c => this.setBgAnim(c)}?
How should I handle this problem in a better way than checking for null?
From docs:
React will call the ref callback with the DOM element when the component mounts, and call it with null when it unmounts. ref callbacks are invoked before componentDidMount or componentDidUpdate lifecycle hooks.
Knowing that at some points ref passed to callback will be a null, just do a check:
setBgAnim(anim) {
this.bgAnim = anim;
if(anim) {
this.bgAnim.play();
}
}
I don't think there is something wrong with such approach.

Multi select dropdown in react native

I am new to react native. Can anyone suggest how do i implement multiple select dropdown in react native. I have tried MultiSelect (https://github.com/toystars/react-native-multiple-select) from react-native-multiple-select but it is not working.
This is a source code of implemented multiselect source list
import React from 'react';
import {View, Text, StyleSheet, FlatList, TouchableHighlight, Dimensions} from 'react-native';
var thisObj;
var deviceHeight = Dimensions.get("window").height;
class MyListItem extends React.PureComponent {
render() {
return (
<View style={{flex: 1}}>
<TouchableHighlight onPress={this.props.onPress.bind(this)} underlayColor='#616161'>
<Text style={this.props.style}>{this.props.item.key}</Text>
</TouchableHighlight>
</View>
);
}
}
export default class MultiSelect extends React.Component {
constructor(props) {
super(props);
var selectedItemsObj = {};
if(this.props.selectedItems) {
var items = this.props.selectedItems.split(',');
items.forEach(function(item) {
selectedItemsObj[item] = true;
});
}
this.state = {
selectedItems: selectedItemsObj
};
}
onItemPressed(item) {
var oldSelectedItems = this.state.selectedItems;
var itemState = oldSelectedItems[item.key];
if(!itemState) {
oldSelectedItems[item.key] = true;
}
else {
var newState = itemState? false: true;
oldSelectedItems[item.key] = newState;
}
this.setState({
selectedItems: oldSelectedItems,
});
var arrayOfSelectedItems = [];
var joinedItems = Object.keys(oldSelectedItems);
joinedItems.forEach(function(key) {
if(oldSelectedItems[key])
arrayOfSelectedItems.push(key);
});
var selectedItem = null;
if(arrayOfSelectedItems.length > 0)
selectedItem = arrayOfSelectedItems.join();
this.props.onValueChange(selectedItem);
}
componentWillMount() {
thisObj = this;
}
getStyle(item) {
try {
console.log(thisObj.state.selectedItems[item.key]);
return thisObj.state.selectedItems[item.key]? styles.itemTextSelected : styles.itemText;
} catch(e) {
return styles.itemText;
}
}
_renderItem = ({item}) => {
return (<MyListItem style={this.getStyle(item)} onPress={this.onItemPressed.bind(this, item)} item={item} />);
}
render() {
return (
<View style={styles.rootView}>
<FlatList style={styles.list}
initialNumToRender={10}
extraData={this.state}
data={this.props.data}
renderItem={this._renderItem.bind(this)}
/>
</View>
);
}
}
const styles = StyleSheet.create({
rootView : {
height: deviceHeight / 2
},
itemText: {
padding: 8,
color: "#fff"
},
itemTextSelected: {
padding: 8,
color: "#fff",
backgroundColor: '#757575'
},
list: {
flex: 1,
}
});
This is how the component should be used
this.state = {
selectedItem : null,
data: [{key:"key1", label:"label1"}, {key:"key2", label:"label2"}]
}
...
<MultiSelect
data={this.state.data}
selectedItems={this.state.selectedItem}
onValueChange={ (itemValue) => thisObj.setState({selectedItem: itemValue})}/>
Selected values will be joined and set in this.state.selectedItem field
I have implemented React Native component.
Source code is attached.
It shows how to make list checkable.
It may be a base for your solution.
Please see.
import React from 'react';
import {View, Text, StyleSheet, FlatList, TouchableHighlight} from 'react-native';
var thisObj;
export default class MultiSelect extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedItems: {}
};
}
onItemPressed(item) {
var oldSelectedItems = this.state.selectedItems;
var itemState = oldSelectedItems[item.key];
if(!itemState) {
oldSelectedItems[item.key] = true;
}
else {
var newState = itemState? false: true;
oldSelectedItems[item.key] = newState;
}
this.setState({
selectedItems: oldSelectedItems,
});
}
componentDidMount() {
thisObj = this;
}
getStyle(item) {
try {
console.log(thisObj.state.selectedItems[item.key]);
return thisObj.state.selectedItems[item.key]? styles.itemTextSelected : styles.itemText;
} catch(e) {
return styles.itemText;
}
}
render() {
return (
<View style={styles.rootView}>
<FlatList style={styles.list}
extraData={this.state}
data={this.props.data}
renderItem={({item}) =>
<TouchableHighlight onPress={this.onItemPressed.bind(this, item)} underlayColor='#f00'>
<Text style={this.getStyle(item)}>{item.key}</Text>
</TouchableHighlight>
}
/>
</View>
);
}
}
const styles = StyleSheet.create({
rootView : {
},
itemText: {
padding: 16,
color: "#fff"
},
itemTextSelected: {
padding: 16,
color: "#fff",
backgroundColor: '#f00'
},
list: {
}
});
How to use this
<Multiselect data = { [{"key" : "item1"}, {"key" : "item2"}{"key" : "item3"}]
}\>

Resources