React native how to animate view opening up->down - reactjs

Consider the following component where the user selects an option from a list:
import React, { Component } from "react";
import PropTypes from "prop-types";
import {
View,
Text,
StyleSheet,
Platform,
FlatList,
TouchableNativeFeedback,
TouchableOpacity,
PLatform
} from "react-native";
import Icon from "react-native-vector-icons/Ionicons";
import { colors, metrics } from "../../themes";
const Touchable =
Platform.OS === "android" ? TouchableNativeFeedback : TouchableOpacity;
class MenuSelector extends Component<{
onSelect: () => any,
config: object,
selected: string
}> {
state = {
listOpened: false
};
handlePress = id => {
this.props.onPress(id);
};
handleSelect = id => {
if (this.props.onSelect) this.props.onSelect(id);
this.setState({
listOpened: false
});
};
handleMenu = () => {
this.setState({
listOpened: !this.state.listOpened
});
};
render = () => {
let title = "";
if (this.props.config) {
title = this.props.config[0].title;
if (this.props.selected) {
let found = this.props.config.find(item => {
return item.id === this.props.selected;
});
if (found) title = found.title;
}
}
let top = (
<View style={styles.header}>
<Text style={styles.text}>{title}</Text>
<Touchable>
<Text style={styles.text}>
<Icon
name={"ios-menu"}
size={20}
onPress={this.handleMenu}
/>
</Text>
</Touchable>
</View>
);
let list = null;
if (this.state.listOpened === true) {
list = (
<FlatList
data={this.props.config}
renderItem={({ item }) => (
<Touchable onPress={this.handleSelect}>
<Text style={[styles.text, styles.listItem]}>{item.title}</Text>
</Touchable>
)}
/>
);
}
return (
<View style={styles.container}>
{top}
{list}
</View>
);
};
}
export default MenuSelector;
const styles = StyleSheet.create({
container: {
flex: -1,
flexDirection: "column"
},
header: {
flex: -1,
flexDirection: "row",
justifyContent: "space-between",
padding: 10,
backgroundColor: "blue"
},
text: {
fontSize: 16,
color: "white",
textAlign: "center",
fontWeight: "bold"
},
listItem: {
padding: 10,
backgroundColor: "blue"
}
});
The component is used in the following context:
let config = [
{
title="Option 1",
id="option1"
},
{
title="Option 2",
id="option2"
},
{
title="Option 3",
id="option3"
},
{
title="Option 4",
id="option4"
},
];
return (
<View style={styles.container}>
<MenuSelector
config={config.options}
selected={this.state.mode}
onPress={this.handleButtonListPress}
/>
<FlatList
data={this.props.data}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => (
<Text style={styles.text}>{item.name}</Text>
)}
/>
</View>
);
As it is, the <MenuSelector> component appears "at once" on screen. I need to add an sliding effect to <MenuSelector>, "pushing down" the data FlatList when appearing on screen...
On closing, same behaviour, but animating from down to up.
How can I add such animation behaviour to my MenuSelector component ?

Related

React Native Gesture Handler + Reanimated Flat List Scroll

I'm having a problem with react-native-gesture handler animation in a FlatList.
when i try to scroll on the flatList the behavior of PanGesture not trigger scroll event.
Reanimated Version - ~2.5
Flatlist
<FlatList
data={test}
ref={flatListRef}
ItemSeparatorComponent={Divider}
style={{
marginTop: 18,
}}
scrollEnabled
renderItem={({ item }) => {
return (
<ActionItem
simultaneousHandlers={flatListRef}
actions={[
{
type: 'UPDATE',
handlePress: () => {
console.log(item.id);
},
},
{
type: 'UPDATE',
handlePress: () => {
console.log(item.id);
},
},
]}
>
<View style={styles.container}>
<Text style={{}}>{item.name}</Text>
</View>
</ActionItem>
);
}}
/>
ActionItem
import { useTheme } from '#hooks';
import React from 'react';
import {
StyleSheet,
View,
Text,
Dimensions,
TouchableOpacity,
} from 'react-native';
import {
PanGestureHandler,
PanGestureHandlerGestureEvent,
} from 'react-native-gesture-handler';
import Animated, {
useAnimatedGestureHandler,
useAnimatedStyle,
useSharedValue,
withTiming,
withSpring,
} from 'react-native-reanimated';
import { IActionItem } from './types';
import Edit from '#assets/svgs/icons/edit.svg';
const threshold = (-Dimensions.get('window').width - 18) * 0.05;
function ActionItem<T>({
children,
actions,
simultaneousHandlers,
}: IActionItem<T>) {
const [actionsWidth, setActionsWidth] = React.useState<number>(0);
const translateX = useSharedValue(0);
const theme = useTheme();
const gestures = useAnimatedGestureHandler<PanGestureHandlerGestureEvent>({
onStart: () => {
translateX.value = withTiming(0);
},
onActive: (e) => {
if (e.translationX < 0) {
translateX.value = withSpring(e.translationX);
}
},
onEnd: (e) => {
const shouldHideActions = e.translationX > threshold;
if (shouldHideActions) {
translateX.value = withTiming(0);
} else {
translateX.value = withSpring(-actionsWidth);
}
},
});
const animatedStyles = useAnimatedStyle(() => {
return {
transform: [
{
translateX: translateX.value,
},
],
};
});
return (
<Animated.View style={styles.container}>
<Animated.View
style={styles.actionsContainer}
onLayout={(e) => {
setActionsWidth(e.nativeEvent.layout.width);
}}
>
{actions?.map((action, index) => {
return (
<TouchableOpacity
key={index}
onPress={action.handlePress}
style={[
{
marginRight: index !== actions.length - 1 ? 20 : 0,
marginLeft: index === 0 ? 20 : 0,
},
]}
>
{action.type === 'UPDATE' && <Edit />}
</TouchableOpacity>
);
})}
</Animated.View>
<PanGestureHandler
simultaneousHandlers={simultaneousHandlers}
onGestureEvent={gestures}
>
<Animated.View style={[animatedStyles, styles.action]}>
{children}
</Animated.View>
</PanGestureHandler>
</Animated.View>
);
}
const styles = StyleSheet.create({
container: {
width: '100%',
elevation: 10,
},
actionsContainer: {
position: 'absolute',
flexDirection: 'row',
alignItems: 'center',
height: '100%',
flex: 1,
right: 0,
},
action: {
backgroundColor: 'red',
},
});
export default ActionItem;
i'm using the flat list from React-Native-Gesture-handler and i'm passing the reference of flatlist to scroll work but doesn't work, anyone have some ideia why ?

React Native: Why does FlatList re-render completely on data changes?

thanks for reading my question! I am struggling with this problem since a few days now: My Flatlist component re-renders all items in the list every time I make a change to the underlying data.
Situation:
I have a FlatList component rendering Items which contain a TouchableOpacity object to toggle Favorite status on this item.
If this button is pressed, I expect only this specific item to change/re-render in my FlatList instead of all items. It feels like as soon as I update state by calling setListData, it re-renders everything.
I have encountered this issue in a more complex setup but was able to drill it down to this core problem. Or is this actually the expected behavior?
Code:
import React, { useState } from "react";
import {
View,
Text,
StyleSheet,
FlatList,
TouchableOpacity,
} from "react-native";
const PlanerScreen = () => {
const [listData, setListData] = useState([
{ id: "1", name: "Banana", isFav: true },
{ id: "2", name: "Apple", isFav: false },
]);
const Item = ({ item, onPressHandler }) => {
console.log(item.name, " rendered");
const color = item.isFav ? "red" : "green";
return (
<View
style={{
flexDirection: "row",
width: "100%",
margin: 10,
}}
>
<Text>{item.name}</Text>
<TouchableOpacity
style={{ width: 100, height: 50, backgroundColor: color }}
onPress={onPressHandler}
/>
</View>
);
};
const favHandler = (id) => {
setListData(
listData.map((item) =>
item.id === id ? { ...item, isFav: !item.isFav } : item
)
);
};
console.log("FlatList rendered");
return (
<View style={{ flex: 1 }}>
<StatusBar style={selectedTheme === "light" ? "dark" : "light"} />
<FlatList
data={listData}
renderItem={({ item }) => (
<Item item={item} onPressHandler={() => favHandler(item.id)} />
)}
keyExtractor={(item) => item.id}
/>
</View>
);
};
export default PlanerScreen;
Console Output on clicking the Favorite Toggle Button:
FlatList rendered
Banana rendered
Apple rendered
FlatList rendered
Banana rendered
Apple rendered
FlatList rendered
Banana rendered
Apple rendered
You can use React.memo which is an alternative to shouldComponentUpdate for functional components.
It tells React when to re-render the component based on prev and next props.
import React, { useState, useCallback } from "react";
import {
View,
Text,
StyleSheet,
FlatList,
TouchableOpacity,
} from "react-native";
const styles = StyleSheet.create({
container: {
flex: 1,
}
})
const keyExtractor = (item) => item.id;
const Item = React.memo(({ item, onPressHandler }) => {
console.log(item.name, " rendered");
const color = item.isFav ? "red" : "green";
return (
<View
style={{
flexDirection: "row",
width: "100%",
margin: 10,
}}
>
<Text>{item.name}</Text>
<TouchableOpacity
style={{ width: 100, height: 50, backgroundColor: color }}
onPress={() => onPressHandler(item.id)}
/>
</View>
);
}, (prevProps, nextProps) => {
if (prevProps.item.isFav === nextProps.item.isFav) return true;
return false;
});
const PlanerScreen = () => {
const [listData, setListData] = useState([
{ id: "1", name: "Banana", isFav: true },
{ id: "2", name: "Apple", isFav: false },
]);
const favHandler = useCallback((id) => {
setListData(prevState => {
return prevState.map((item) =>
item.id === id ? { ...item, isFav: !item.isFav } : item
)
}
);
}, []);
console.log("### FlatList rendered #####");
return (
<View style={styles.container}>
<FlatList
data={listData}
renderItem={({ item }) => <Item item={item} onPressHandler={favHandler} />}
keyExtractor={keyExtractor}
/>
</View>
);
};
export default PlanerScreen;

Flatlist with calling API on scroll does not work properly

I am using Function component in react native.
In that I am use Flatlist for data view. on onEndReached function of FlatList i am calling API for fetching data.
My Problem is
on scroll API call twice some times
on the Finish of all data. ActivityIndicator is showing in ListFooterComponent function.
Can you please help me.
Thanks in Advance.
Here is my code:
import React, { useState, useEffect } from "react";
import {
View,
StyleSheet,
ActivityIndicator,
Image,
FlatList,
TouchableHighlight,
} from "react-native";
import MainStyle from "../constants/Style";
import Layout from "../constants/Layout";
import Colors from "../constants/Colors";
import Services from "../Services";
const LIMIT_PER_PAGE = 10;
let fetching = false;
let isSubscribed = true;
let isListEnd = false;
let pageNo = 1;
var sortby = "latest";
let width = Layout.window.width / 2 - 16;
export default function PostListScreen(props) {
const [posts, setPosts] = useState([]);
fetchAllLatestPost = function () {
console.log("fetchAllLatestPost", fetching, isListEnd);
if (!fetching && !isListEnd) {
fetching = true;
Services.getAllPost(sortby, pageNo, LIMIT_PER_PAGE)
.then(function (res) {
if (!!res) {
console.log("res.length", res.length)
if (!!res.length && isSubscribed) {
pageNo++;
setPosts((posts) => {
posts = [...posts, ...res];
return posts;
});
} else {
isListEnd = true;
}
fetching = false;
}
});
}
};
useEffect(() => {
fetching = false;
isListEnd = false;
pageNo = 1;
isSubscribed = true;
fetchAllLatestPost();
return () => (isSubscribed = false);
}, []);
let readyImage = function (post) {
if (!!post.urls && !!post.urls.regular) {
return typeof post.urls.regular === "string"
? { uri: post.urls.regular }
: post.urls.regular;
} else {
return require("../assets/images/logo_white.png");
}
};
let renderItem = function (post, index) {
return (
<TouchableHighlight onPress={() => onPressPost(post)} key={index}>
<View style={styles.item}>
<Image
source={readyImage(post)}
style={{
flex: 1,
width: null,
height: null,
resizeMode: "cover",
borderRadius: 4,
}}
/>
</View>
</TouchableHighlight>
);
};
let renderFooter = function () {
return (
<View style={styles.footer}>
{console.log("fetching in footer ", fetching)}
{!!fetching ? (
<ActivityIndicator color="white" style={{ margin: 15 }} />
) : null}
</View>
);
};
return (
<View style={MainStyle.wrapper}>
<FlatList
numColumns={2}
keyExtractor={(item, index) => index.toString()}
data={posts}
onEndReached={() => fetchAllLatestPost()}
onEndReachedThreshold={0.5}
renderItem={({ item, index }) => <View>{renderItem(item, index)}</View>}
getItemLayout={(data, index) => ({
length: width,
offset: (width/2) * index,
index,
})}
ListFooterComponent={renderFooter()}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flexDirection: "row",
flexWrap: "wrap",
},
item: {
height: width,
width: width,
margin: 8,
},
footer: {
padding: 10,
justifyContent: "center",
alignItems: "center",
flexDirection: "row",
},
});
This is how I handled it
<FlatList
data={this.state.posts}
onRefresh={() => {
if (!this.props.posts.fetching) {
this.setState({
page: 1,
checkData: true,
posts: [],
});
this.props.postsRequest({page: 1});
}
}}
// onEndReached={}
keyExtractor={item => item.uid}
onMomentumScrollEnd={() => {
if (!this.props.posts.fetching) {
this.props.postsRequest({page: this.state.page + 1});
this.setState({page: this.state.page + 1, checkData: true});
}
}}
onEndReachedThreshold={0}
refreshing={this.props.posts.fetching}
showsVerticalScrollIndicator={false}
contentContainerStyle={{paddingBottom: 50}}
style={{padding: 10, marginVertical: 10}}
renderItem={item => (
<View style={{marginVertical: 5}}>
<Posts post={item.item} navigation={this.props.navigation} />
</View>
)}
/>

React native Tried to get frame for out of range index NaN (MYSQL database)

So I started learning react-native from videos and they have used ListView but as the ListView will be deprecated soon and will be removed. I get to know that FlatList will be the proper replacement but being a beginner I am not able to migrate to Flatlist.
Error message
ListView has been removed from React Native.See link for more information or use 'deprecated-react-native-listview'
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View,
FlatList,
ActivityIndicator,
} from 'react-native';
import { createStackNavigator, createAppContainer } from 'react-navigation';
export default class Login extends Component {
static navigationOptions= ({navigation}) =>({
header: null
});
state = {
username : [],
data : []
}
constructor(props) {
super(props);
this.state = {
isLoading: true, // check if json data (online) is fetching
dataSource: [], // store an object of json data
};
}
componentDidMount () {
return fetch("http://172.16.2.109:8090/assessment/getdata2.php?username=test2312")
.then((response) => response.json())
.then((responseJson) => {
// set state value
this.setState({
isLoading: false, // already loading
dataSource: responseJson
});
})
.catch((error) => {
ToastAndroid.show(error.toString(), ToastAndroid.SHORT);
});
}
render() {
const { navigate } = this.props.navigation;
if(this.state.isLoading) {
return(
<View style={{flex: 1, padding: 20}}>
<ActivityIndicator/>
</View>
)
}
return(
<View style={{flex: 1, paddingTop:20}}>
<FlatList
data={this.state.dataSource}
renderItem={({item}) => {
return (
<View>
<Text style={styles.info}>{item.ascendant} is </Text>
</View>
)
}}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
}
const styles = StyleSheet.create({
info: {
fontSize: 20,
}
});
Hope this helps!
import { FlatList } from 'react-native';
<FlatList
data={this.state.dataSource}
showsVerticalScrollIndicator={false}
renderItem={(rowData, index) => (
<Text style={styles.rowViewContainer} onPress={this.GetListViewItem.bind(this, rowData.fruit_name)}>{rowData.fruit_name}</Text>
<View
style={{
height: .5,
width: "100%",
backgroundColor: "#000",
}}
/>
)}
keyExtractor={(item, index) => index.toString()}
style={{marginTop: 10}}
/>
try this code
import { AppRegistry, StyleSheet, FlatList, Text, View, Alert, ActivityIndicator, Platform} from 'react-native';
class Project extends Component {
constructor(props)
{
super(props);
this.state = {
isLoading: true
}
}
componentDidMount() {
return fetch('https://reactnativecode.000webhostapp.com/FruitsList.php')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson
}, function() {
// In this block you can do something with new state.
});
})
.catch((error) => {
console.error(error);
});
}
FlatListItemSeparator = () => {
return (
<View
style={{
height: 1,
width: "100%",
backgroundColor: "#607D8B",
}}
/>
);
}
GetFlatListItem (fruit_name) {
Alert.alert(fruit_name);
}
render() {
if (this.state.isLoading) {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ActivityIndicator />
</View>
);
}
return (
<View style={styles.MainContainer}>
<FlatList
data={ this.state.dataSource }
ItemSeparatorComponent = {this.FlatListItemSeparator}
renderItem={({item}) => <Text style={styles.FlatListItemStyle} onPress={this.GetFlatListItem.bind(this, item.fruit_name)} > {item.fruit_name} </Text>}
keyExtractor={(item, index) => index}
/>
</View>
);
}
}
const styles = StyleSheet.create({
MainContainer :{
justifyContent: 'center',
flex:1,
margin: 10,
paddingTop: (Platform.OS === 'ios') ? 20 : 0,
},
FlatListItemStyle: {
padding: 10,
fontSize: 18,
height: 44,
},
});
AppRegistry.registerComponent('Project', () => Project);

React Native Navigation with React Native Admob About

I created a 3 page application with React native navigation. Admob ads are on the 3rd page. I want to try the same ad code on all three screens. If there is any idea in this matter, please share. Thank you.
For better understanding I give the following expo code.
import React, { Component } from 'react';
import {
WebView,
AppRegistry,
StyleSheet,
Text,
View,
Button,
Alert
} from 'react-native';
import { StackNavigator } from 'react-navigation';
import ListComponent from './ListComponent';
class App extends Component {
static navigationOptions = {
title: 'App',
};
OpenSecondActivityFunction = () => {
this.props.navigation.navigate('Second');
};
render() {
return (
<View style={styles.container}>
<Button
onPress={this.OpenSecondActivityFunction}
title="Open Second Activity"
/>
</View>
);
}
}
class SecondActivity extends Component {
static navigationOptions = {
title: 'SecondActivity',
};
OpenThirdActivityFunction = data => {
this.props.navigation.navigate('Third');
};
render() {
return (
<View style={{ flex: 1 }}>
<ListComponent
OpenThirdActivityFunction={this.OpenThirdActivityFunction}
/>
</View>
);
}
}
class ThirdActivity extends Component {
static navigationOptions = {
title: 'ThirdSecondActivity',
};
render() {
return (
<View style={{ flex: 1 }}>
<Text>3</Text>
</View>
);
}
}
const ActivityProject = StackNavigator({
First: { screen: App },
Second: { screen: SecondActivity },
Third: { screen: ThirdActivity },
});
export default ActivityProject;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
}
});
Listcomponent.js
import React, { Component } from 'react';
import {
AppRegistry,
View,
Text,
FlatList,
ActivityIndicator,
} from 'react-native';
import { List, ListItem, SearchBar } from 'react-native-elements';
class ListComponents extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
page: 1,
seed: 1,
error: null,
refreshing: false,
};
}
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '98%',
backgroundColor: '#CED0CE',
marginLeft: '2%',
}}
/>
);
};
renderHeader = () => {
return <SearchBar placeholder="Type Here..." lightTheme round />;
};
renderFooter = () => {
if (!this.state.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: '#CED0CE',
}}>
<ActivityIndicator animating size="large" />
</View>
);
};
render() {
return (
<List containerStyle={{ borderTopWidth: 0, borderBottomWidth: 0 }}>
<FlatList
data={[{ name: 1, coders: 2 }]}
renderItem={({ item }) => (
<ListItem
roundAvatar
title={`${item.name}`}
subtitle={item.coders}
containerStyle={{ borderBottomWidth: 0 }}
onPress={() => this.props.OpenThirdActivityFunction(item.coders)}
/>
)}
keyExtractor={item => item.coders}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
/>
</List>
);
}
}
export default ListComponents;

Resources