how to make parallax scroll view with tabs in React Native? - reactjs

i currently try to make some scroll view with tab bar.
and if i scroll down, tab bar is fixed on the top.
this is the example i want.
https://i.stack.imgur.com/ITZHJ.gif
if you know how to make this or NPM i can use, please let me know if you have it.
thanks for reading!!
this is i try by my self but failed..
<Animated.ScrollView
scrollEventThrottle={5}
showsVerticalScrollIndicator={false}
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {y: this.nScroll}}}],
{useNativeDriver: true},
)}
style={{zIndex: 0}}>
<Animated.View
style={{
transform: [
{translateY: Animated.multiply(this.nScroll, 0.65)},
{scale: this.imgScale},
],
backgroundColor: THEME_COLOR,
}}>

You might use FlatList that have stickyHeaderIndices={[0]} for keeping ListHeaderComponent sticky, and the data passed in depend on tab state
const [ tab, setTab ] = useState(1)
// you should add at least keyExtractor and renderItem props...
<FlatList
stickyHeaderIndices={[0]}
ListHeaderComponent={ <Tabs setTab={setTab} /> } // create `Tabs` component of the nav, use `props.setTab` to change `tab` state
data={ tab == 1 ? tab1data : tab2data } // assuming tab1data is array
/>
more about FlatList https://reactnative.dev/docs/flatlist

Related

React Native FlatList: how to reset scroll position

I have a horizontal FlatList in my React Native iOS app. At the bottom of the screen, the FlatList is displayed with 5 items. If you click on the last item (scrolled all the way to the right), the screen will refresh and the FlatList will remain scrolled to the right. How can I get the FlatList to always reset the scroll position (to scroll from the beginning) when the screen changes?
Edit: I have a feeling that my screen is not actually "refreshing" but rather merely changing the data shown on the screen. In this case I may need to trigger a refresh of the screen somehow to cause the FlatList to reset the scroll position? Any help would be greatly appreciated!
const HorizontalScroll = ({items, handlePress}) => {
const renderItem = ({item}) => {
const itemData = { color: item.color, title: item.title };
return <HorizontalItem itemData={itemData} handlePress={handlePress} />;
};
return (
<View style={styles.container}>
<FlatList
data={items}
renderItem={renderItem}
horizontal={true}
numColumns={1}
key={(item) => item.id}
initialNumToRender={5}
scrollToIndex={0}
ItemSeparatorComponent={() => <View style={{width: 8}} />}>
</FlatList>
</View>
);
};
I solved this problem by forcing a refresh of the component. Specifically, I added a key prop to my component so that every time the key value changes, it remounts the component. This causes the FlatList to reset the scroll position to the beginning.
<View key={items[0].title}>
{content}
</View>

React Native - stickyHeaderIndices not working for SectionList

I am trying to make some header to stick to the top of the SectionList but it seems like the stickyHeaderIndices={[0]} doesn't work as expected. This is the section list:
<SectionList
ref={(ref) => (productsSectionListRef = ref)}
stickyHeaderIndices={[0]}
ListHeaderComponent={_renderHeader()}
contentContainerStyle={[style.products_container]}
sections={productListSections}
renderItem={({item, section}: {item: string; section: any}) => {
return _renderProduct(item);
}}
stickySectionHeadersEnabled={false}
renderSectionHeader={({section}) => {
return _renderProductHeader(section.title);
}}
keyExtractor={(item) => item}
showsVerticalScrollIndicator={false}
viewabilityConfig={{
itemVisiblePercentThreshold: 50,
}}
onViewableItemsChanged={_onViewableItemsChanged}
bounces={false}
scrollEventThrottle={4}
initialNumToRender={productListSections.length}
scrollEnabled={scrollEnabled}
removeClippedSubviews={true}/>
I tryed to put the header outside the section list but the header it's bigger and I cannot scroll inside of that header component.
stickyHeaderIndices={[0]}
will make first item in flatlist sticky to the top
if you change to
stickyHeaderIndices={[5]}
the 4th item will stick to top after the item reached to the top when scrolled
item in
ListHeaderComponent={_renderHeader()}
will be on top of the flatlist but not act as item inside flatlist its independent

ScrollView positions being restarted on a child component re-render (update)

I am having an issue (this is probably something I am ignoring within the render cycle) maintaining the position of two different scroll views when some of its child components are updated.
I have a hierarchy of views that is like:
<ScrollView vertical scrolling ability>
...
<ScrollView horizontal and vertical scrolling ability>
...
<Matrix>
<Cell updateCellStatusHandler={handler}>
</Matrix>
...
</ScrollView>
</ScrollView>
So, the updates on the internal cells, are resetting both scrolls on cell status update and this generates a super weird experience with the user having to scroll down/left/right back to continue interacting with the Matrix of cells with a status I have.
I have tried to save the scrollOffset (x,y) using useState but if I change some cell, the state is reseted to (0,0) which is my initial state.
const [scrollOffset, setScrollOffset] = useState({
scrollX: 0,
scrollY: 0,
})
But without luck.
<ScrollView
{...props}
scrollEventThrottle={16}
ref={scrollReference}
// tslint:disable-next-line: jsx-no-lambda
onScroll={event => {
setScrollOffset({
scrollX: event.nativeEvent.contentOffset.x,
scrollY: event.nativeEvent.contentOffset.y,
})
}}
onScrollEndDrag={event => {
console.log(event.nativeEvent.contentOffset.y)
console.log(event.nativeEvent.contentOffset.x)
}}
>
{props.children}
</ScrollView>
One possible approach to solve this is to have a mechanism that allow me to save the scroll position before the update. But this will complicate a lot the communication between components, etc.
By the way, the cell status update is being handled via Redux.
If some of you can bring you some light over this, would be great.
---- UPDATE 1 (Code of the panel component added) ----
Parent component is:
<View style={styles.container}>
<CustomScrollView enableResetScrollToCoords={false}>
<Section>
<WhiteContainer>
<View style={styles.rateContainer}>
<DescriptionTextStatus
statusText={getMessageForRate(availabilityRate)}
descriptionText={getDescriptionForRate(
availabilityRate,
isTeacher,
)}
icon={getImageForRate(availabilityRate)}
/>
</View>
</WhiteContainer>
</Section>
{!loggedUserIsTeacher() && <AvailabilityStart />}
<AvailabilityPanel />
<AvailabilityStatus />
<AvailabilityButtonSave />
</CustomScrollView>
</View>
Availability Panel is one of the childs
export const AvailabilityPanel: React.FunctionComponent<{}> = () => {
const panel: Cell[][] = useSelector((state: ReduxStore) => {
return get(state.entities, 'availability.panel', undefined)
})
if (panel === undefined) {
return <Nothing />
}
return (
<Section>
<WhiteContainer>
<LinearGradient
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
colors={[Palette.White, Palette.White68]}
>
<View style={styles.rateContainer}>
<DescriptionTextStatus
statusText={strings.warning}
descriptionText={strings.selectionMessage}
icon={'clock'}
/>
<View style={styles.separator} />
</View>
<View style={styles.container}>
<ScrollView
style={styles.scrollView}
directionalLockEnabled={false}
horizontal={true}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
>
<View style={styles.contentContainer}>
<View style={styles.weekdaysContainer}>
<PanelHeader weekdays={weekdays} />
</View>
<View style={styles.rowsContainer}>
{hours.map((hourLabel: string, index: number) => {
return (
<PanelRow
key={index}
hourLabel={hourLabel}
hoursRow={panel[index]}
rowIndex={index}
/>
)
})}
</View>
</View>
</ScrollView>
</View>
</LinearGradient>
</WhiteContainer>
</Section>
)
}
Thanks in advance.
ScrollView does not reset scroll position because of child updates, regardless of whether you use Redux or not. You don't need to try to patch it with workarounds like saving current scroll positions. Rather just figure out what's wrong with your setup
It's hard to tell because you didn't provide your actual code, but I'm pretty sure one or both ScrollViews are remounted on updates (old ScrollView is removed and new one is created in its place with its own scroll position). It's likely that you are declaring new components inside render function so React will just remount them every time
I think the overall idea of saving the scroll position of your scrollview is good.
I think the fact that your state is reset to 0 is probably due to Redux (not sure however, could you precise how your component is connected to redux ?). I am not so familiar with Hooks, but in a React Class component, I would try to save the scroll position in a non state property of the class. Maybe save it in a variable outside of your functional component ?
Then in the handler to manage cell updates, you could ensure that your scrollview scrolls to the position you saved (using ScrollView.scrollTo(), with {animated: false} option so that the animation is not visible for the user)

Navigate to same screen with different parameters using - ReactNavigation : React Native

I have been trying to navigate to the same screen with different parameters on react-native application. It is a category screen, where I don't want the user to go back and select different categories to view products. I have made a screen where products from categories are displayed.
Below is my code:
<View style={styles.horizontalSlider}>
<FlatList
horizontal
showsHorizontalScrollIndicator={false}
data={this.state.DataSource}
keyExtractor={(item, index) => index.toString()}
extraData={this.state.selectedItem}
renderItem= { ({item}) => (
<TouchableOpacity onPress={(category) => navigate.push('CategoryScreen', {item})}>
<ImageBackground style={styles.profileImgContainer} >
<Image source={{ uri: item.images }} style={[styles.profileImg, , { borderColor: 'green', borderWidth:2 }]} />
</ImageBackground>
</TouchableOpacity>
)}
/>
</View>
If you want to navigate to the same screen with different parameters and be unable to go back, use this.props.navigation.replace('CategoryScreen', { ...new params here... })
If you want to change current navigation params without navigating, use this.props.navigation.setParams()
(navigation prop)
But your problem is not a navigation problem. You don't need multiple screens for that. Solve it with react. All you need to do is to render your list conditionally depending on what category (or circle item?) is currently selected. Store this information in state and use it in render
You can add unique key:
this.props.navigation.navigate({ routeName: route, params: params, key: route + params.id })

React Navigation Show/Hide Drawer Items

React Native using React Navigation - Show/Hide Drawer Item
I was wondering if you guys or maybe someone have come up of a better Idea of showing or hiding some menu or Drawer Item under DrawerNavigator.
Basically I have user roles and I want to show/hide selected menu's based on user's role.
My setup now is that I have A DrawerNavigator nested within a StackNavigator.
Menu That Contains My Drawer Items
Hide some drawer items based on user role
Currently Using:
react version 16.0.0-alpha.12
react-native version 0.46.0
react-navigation version 1.0.0-beta.11
You can create your own custom component that will be rendering drawer items
contentComponent: CustomDrawerContentComponent
inside that component you can define logic on show hide your items
example:
const CustomItems = ({
navigation: { state, navigate },
items,
activeItemKey,
activeTintColor,
activeBackgroundColor,
inactiveTintColor,
inactiveBackgroundColor,
getLabel,
renderIcon,
onItemPress,
style,
labelStyle,
}: Props) => (
<View style={[styles.container, style]}>
{items.map((route: NavigationRoute, index: number) => {
const focused = activeItemKey === route.key;
const color = focused ? activeTintColor : inactiveTintColor;
const backgroundColor = focused
? activeBackgroundColor
: inactiveBackgroundColor;
const scene = { route, index, focused, tintColor: color };
const icon = renderIcon(scene);
const label = getLabel(scene);
return (
<TouchableOpacity
key={route.key}
onPress={() => {
console.log('pressed')
onItemPress({ route, focused });
}}
delayPressIn={0}
>
<View style={[styles.item, { backgroundColor }]}>
{icon ? (
<View style={[styles.icon, focused ? null : styles.inactiveIcon]}>
{icon}
</View>
) : null}
{typeof label === 'string' ? (
<Text style={[styles.label, { color }, labelStyle]}>{label}</Text>
) : (
label
)}
</View>
</TouchableOpacity>
);
})}
</View>
);
So in the code above you can define which route will be shown, for instance you can have store with list of items show or hide and from here you can make comparison.
Hope it helps

Resources