How to autoplay a React Native ScrollView? - react-native

I'm trying to make a ScrollView to autoplay itself and have tried the below implementation:
componentDidMount() {
this._interval = setInterval(() => {
this._scrollViewX+=1;
this._scrollView.scrollTo({x: this._scrollViewX, y: 0, animated: true}) },
800
);
}
Render():
<ScrollView
ref={(c) => {this._scrollView = c;}}
horizontal={true}>
<View style={{paddingTop: '3%', flex: 1, width: 900, flexDirection: "row", flexWrap: 'wrap'}}>
{this.props.data.map((item, index) => {
return (
<View
style={{margin:4, marginHorizontal:12}}
key={index}>
<TextItemToggle
value={item}
onPress={(value)=>{this._onPressHandler(value)}}
/>
</View>
)
})}
</View>
</ScrollView>
I have also tried an increment of 0.1 with 16ms interval. They all seemed a bit laggy. Is there any better way to implement this?

You do it on ComponenDidMount() which will take into consideration the whole component life cycle to update screen. I am sure you can not control the speed of scrolling. You either animated:true or false and that's it.

Related

Last Image is being cropped, when using ScrollView in react-native

I am trying to build something like instagram posts, that is continuous images that can be scrolled. But the last image is being cropped, that is only the upper half of it is being visible, there are several posts, regarding the same, but those didnt help, (contentContainerStyle={{flexGrow: 1,}}, adding height to a invisible view). Can someone please point out what is going wrong?
EDIT: I have changed scrollview to flatlist and still face the same problem, can you suggest what else to do?
EDIT 2: realised that the <Header /> and <Stories /> above the flatlist are not letting it scroll completely, that is the height that
it is not scrolling is proportional to height of <Header /> and <Stories />
post.js
const Post = ({post}) => {
return (
<View style={{flex:1}}>
<Divider width = {0.5}/>
<PostHeader post={post}/>
<PostImage post={post} />
<PostFooter post={post}/>
</View>
)
}
const PostImage = ({post}) => {
return (
<View style={styles.postContainer}>
<Image style={styles.image} source={{uri: post.post_url}}></Image>
</View>
)
}
const styles = StyleSheet.create({
container: {
},
dp: {
width: 35,
height: 35,
margin:5,
borderRadius: 20,
borderWidth : 1,
borderColor : '#ff8501'
},
postContainer: {
width: '100%',
height: 400,
},
image: {
height: '100%',
resizeMode: 'cover',
}
})
homescreen.js
const HomeScreen = () => {
return (
<SafeAreaView >
<Header />
<Stories />
{/* <ScrollView>
{
POSTS.map((post, index) => {
return (
<Post key={index} post={post} />
)
})
}
</ScrollView> */}
<FlatList data={POSTS} renderItem={({item}) => <Post post={item} />} />
</SafeAreaView>
)
}
If you want to render repetitive view so why you are not using Faltlist instead of Scrollview. For repetitive view react native provide one component which is called Flatlist and pass you array data in render item it will give you better performance as well.
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</SafeAreaView>
const renderItem = ({ item }) => (
<Divider width = {0.5}/>
<PostHeader post={item}/>
<PostImage post={item} />
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
},
item: {
backgroundColor: '#f9c2ff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
});
According to React Native docs FlatList is the Component you should use:
ScrollView renders all its react child components at once, but this has a performance downside.
Imagine you have a very long list of items you want to display, maybe several screens worth of content. Creating JS components and native views for everything all at once, much of which may not even be shown, will contribute to slow rendering and increased memory usage.
This is where FlatList comes into play. FlatList renders items lazily, when they are about to appear, and removes items that scroll way off screen to save memory and processing time.
FlatList is also handy if you want to render separators between your items, multiple columns, infinite scroll loading, or any number of other features it supports out of the box.
const Post = () => {
renderItemHandler = ({post, index}) => (
<View key={index} >
<Divider width={0.5}/>
<PostHeader post={post}/>
<PostImage post={post} />
</View>
)
return (
<SafeAreaView style={{flex: 1}}>
<View style={{height: "90%"}}>
<Flatlist
data={POSTS}
renderItem={renderItemHandler}
keyExtractor={item => item.id}
/>
</View>
</SafeAreaView>
)
}

How to create a sticky tab selector with nested scrollviews like twitter or instagram profile screens [REACT-NATIVE]

I'm trying to create a ScrollView which contains one sticky selector, that allow the selection between two nested ScollViews. It's like the twitter profile screen, or the instagram screen, where you can switch between my posts and posts where I was tagged.
Now my problem actually is that this two nested ScollViews, let's say "MY POSTS" and "TAGGED" could have different sizes, but the RootScrollView consider only the biggest height of the two scrollviews, so if in the first I've 20 items, and let's say height=1000, in the second if I don't have items, or less items, I'll have an empty space y offset like the first.
I know it's not so clear, but if you open instagram or twitter profile screens you'll immediately get it, the problem of the different heights.
Now as you'll see, what I've tried to do is create a RootScrollView, put inside it two views, the header and the sticky selector, in twitter it's the "Tweet", "Tweets and replies" ... , and the a NestedScrollView which initially has scrollEnabled=false, and then, by scroll the root I'll update it to true and to false the root one. But it seems not to work correctly.
Here's the code:
const HEADER_HEIGHT = height / 3;
const STIKY_SELECTOR_HEIGHT = 100;
const App = () => {
const rootScrollRef = useRef();
const nestedScrollRef = useRef();
const [offset, setOffset] = useState(0);
const [scrollEnabled, setScrollEnabled] = useState(false);
const onRootScroll = ({
nativeEvent: {
contentOffset: { y },
},
}) => {
const direction = y > offset ? "down" : "up";
setOffset(y);
if (y > HEADER_HEIGHT - 10 && direction == "down") {
setScrollEnabled(true);
}
};
const onNestedScroll = ({
nativeEvent: {
contentOffset: { y },
},
}) => {
if (y < 20) setScrollEnabled(false);
};
const renderItem = () => {
return <View style={styles.cell} />;
};
return (
<View style={{ flex: 1 }}>
{/* ROOT SCROLLVIEW */}
<ScrollView
simultaneousHandlers={nestedScrollRef}
scrollEventThrottle={16}
ref={rootScrollRef}
onScroll={onRootScroll}
stickyHeaderIndices={[1]}
scrollEnabled={!scrollEnabled}
style={{ flex: 1, backgroundColor: "gray" }}
>
{/* HEADER */}
<View
style={{ width, height: HEADER_HEIGHT, backgroundColor: "darkblue" }}
></View>
{/* STIKY SELECTOR VIEW */}
<View
style={{ height: STIKY_SELECTOR_HEIGHT, backgroundColor: "red" }}
></View>
{/* NESTED SCROLLVIEW */}
<View style={{ height: height - STIKY_SELECTOR_HEIGHT }}>
<FlatList
data={[1, 2, 3, 4, 5, 6, 7]}
ref={nestedScrollRef}
scrollEventThrottle={16}
onScroll={onNestedScroll}
scrollEnabled={scrollEnabled}
renderItem={renderItem}
numColumns={2}
contentContainerStyle={{
justifyContent: "space-between",
}}
/>
</View>
</ScrollView>
</View>
);
};
If someone is facing the same problem there a component for that react-native-collapsible-tab-view
<Tabs.Container
renderHeader={Header}
headerHeight={HEADER_HEIGHT} // optional>
<Tabs.Tab name="A">
<Tabs.FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={identity}
/>
</Tabs.Tab>
<Tabs.Tab name="B">
<Tabs.ScrollView>
<View style={[styles.box, styles.boxA]} />
<View style={[styles.box, styles.boxB]} />
</Tabs.ScrollView>
</Tabs.Tab>
</Tabs.Container>

Component only reachable by scrolling regardless of window size

I'm trying to make a layout so that the later parts of the view are only reachable by scrolling.
Currently I'm using Dimensions to generate Views with the correct height. Is there a better way of doing so? My current solution doesn't seem too correct.
export default function MyApp() {
const height = Dimensions.get('window').height;
return (
<View style={styles.container}>
<ScrollView>
<View style={{backgroundColor: 'green', height:height}}/>
<View style={{backgroundColor: 'red', height:40}}/>
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container:{
backgroundColor: 'white',
flex: 1
}
});
You can use VirtualizedList component, for example as
<VirtualizedList
data={['body']}
renderItem={({ item }) => (
<View style={styles.screen}>
{/* Put more content for body */}
</View>
)}
keyExtractor={(item, index) => index.toString()}
getItemCount={() => {
return 1;
}}
getItem={(data, index) => {
return data[index];
}}>
</VirtualizedList>
Your solution work, but not good and it have downside, when you change your phone orientation to landscape there will be bug. I dont like using Dimensions in my code unless there is no other way or use Dimensions addEventListener to listen window size and update component whenever window size change. I will suggest you a better way.
First, create a component called LayoutSizeAwareView, after this view rendered, we will catch it size from onLayout props and use them to render it children.
const LayoutSizeAwareView = (props) => {
const [size, setSize] = React.useState({width: 0, height: 0});
return (
<View
...props,
onLayout={(e) => {
setSize({
width: e.nativeEvent.layout.width,
height: e.nativeEvent.layout.height,
})
props.onLayout(e)
}}
>
{props.children(size)}
</View>
)
}
And then, in your case, use it like this
export default function MyApp() {
return (
<LayoutSizeAwareView style={styles.container}>
{({width, height}) => {
return (
<ScrollView>
<View style={{backgroundColor: 'green', height: height}}/>
<View style={{backgroundColor: 'red', height: 40}}/>
</ScrollView>
)
}}
</View>
);
}
const styles = StyleSheet.create({
container:{
backgroundColor: 'white',
flex: 1
}
});
This way your code look even cooler, there will be some typo in my code since I dont have IDE here, but you might get the idea.

ReactNative - FliatList turns white screen when render many item

Android : 4.4.2 , 8.0
React-native : 0.61.4
It only happens on android.
https://gfycat.com/flimsyastonishingguineafowl
<FlatList
style={{ transform: [{ scaleY: -1 }], marginBottom: 10 }}
removeClippedSubviews
initialNumToRender={20}
data={replyList}
onEndReached={this.nextPage}
contentContainerStyle={{ justifyContent: 'flex-end', flexGrow: 1 }}
keyboardShouldPersistTaps="handled"
pinchGestureEnabled={false}
keyboardDismissMode="none"
keyExtractor={item => `${item.replyUID}reply`}
showsVerticalScrollIndicator={false}
renderItem={({ item }) => (
<RenderReplyItem
item={item}
/>
)}
onScroll={handleScroll}
ref={setRef}
scrollEventThrottle={16}
/>
export default class RenderReplyItem extends PureComponent {
render() {
const {
item,
} = this.props;
return (
<View style={[styles.replyContainer, { alignSelf: 'center', transform: [{ scaleY: -1 }] }]}>
<View style={styles.replyBackground}
>
<Text style={styles.replyContentStyle}
>
{item.content}
</Text>
<View style={styles.replyBottom}>
<Text style={styles.replyregisterDate}>
{item.replyUID}
</Text>
</View>
</View>
</View>
);
}
}
The screen turns white after 80 items have been rendered.
Then scroll down again and the screen will come back and the scroll will move randomly.
Other FlatLists experienced a similar phenomenon and disappeared after setting keyExtractor.
However, only after this page, the same thing happens after you set the keyExtractor.
duplicate of
alternative of flatlist in react-native with much improved performance
For improving flatlist performance tips see here

RenderFooter called infinitely

When I reach the bottom of FlipKarts recyclerlistview it enters an infinite loop calling renderFooter thousands of times and I cant scroll up anymore.... No idea what this could be. It should scroll to the bottom and then simply bounce up or just stay at the bottom. Instead it gets stuck and Ive found nothing on github or stackoverflow.
renderFooter() {
if(this.state.outPosts == false) {
return <View style={{
alignContent:'center',
justifyContent:'center',
alignItems:'center',
}}>
<ActivityIndicator
style={{
marginTop:15*factor_ver,
marginBottom:15*factor_ver,
}}
size={"small"}
color={'#9b9b9b'}
/>
</View>
}
else if(this.state.outPosts == true) {
return <View style={{
height:140*factor_hor,
marginTop:20,
alignContent:'center',
justifyContent:'center',
alignItems:'center',
}}>
<Text style={{textAlign:'center', fontSize:22*factor_hor, fontFamily:'Avenir Next', color:'#9b9b9b',}}>
No posts! :(
</Text>
<View style={{flex:1,}}></View>
<Block width={60*factor_hor}
height={60*factor_hor}
style={{
justifyContent:'center',
alignContent:'center',
flex: 1,
}}
/>
<View style={{height:30}}></View>
</View>
}
}
<RecyclerListView
style={{flex:1, backgroundColor:'#f7f7f7', paddingTop:8*factor_hor,}}
extendedState={this.state}
rowRenderer={this._renderRow}
dataProvider={this.state.dataProvider}
layoutProvider={this._layoutProvider}
showsVerticalScrollIndicator={false}
onEndReached={() => this.fetchPosts()}
onEndReachedThreshold={500}
renderFooter={this.renderFooter}
onScroll={(e) => this.handleScroll(e)}
scrollThrottle={250}
refreshControl={
<RefreshControl
tintColor={'white'}
refreshing={false}
onRefresh={() => {
this.setState({isLoad:true}), this.refresh_feed()}}
/>}
/>
great so Im going to leave this up here because theres nothing else I could find to explain this. If youre using React native FlipKart recyclerlistview and you render empty views then when you scroll to the bottom this glitch happens where you cant scroll up and renderFooter is called infinitely. Check to make sure each rendered view has at least a container to fill it.