Add header to Recyclerlistview
same this :
https://medium.com/appandflow/react-native-scrollview-animated-header-10a18cb9469e
Thanks
Pass your own ScrollView with the header added already. That would be an easy way. We don't support it by default because measuring header will cause an extra frame skip. Those who want it should do it themselves and then offset its height using distanceFromWindow
@naqvitalha I'm using an externalScrollView to render a header like this
{renderHeader && renderHeader()}
<ScrollViewer
ref="refS"
{...this.props}
onSizeChanged={() => {
/* Do nothing */
}}
/>
The problem here is that the header is always on top of the list while scrolling.
What did you mean when you said offset the height using distanceFromWindow?
I added one by going into:
node_modules\recyclerlistview\dist\reactnative\platform\reactnative\scrollcomponent\ScrollComponent.js
And adding the following line as line 53 (after "} }, ")
this.props.renderHeader ? this.props.renderHeader() : null,
Maybe not good practise but has worked for me so far. This means the header 'scrolls away', instead of always being fixed at the top of the list.
@tafelito
What did you mean when you said offset the height using
distanceFromWindow?
It's a prop of RecyclerListView that you can use, see here.
Note that your other issue ("The problem here is that the header is always on top of the list while scrolling.") is caused by rendering your header outside of the ScrollView - you want it to be IN the ScrollView.
I also had this issue and my solution was to add the header as the first item of the list and gave it its own ViewType, and it worked perfectly.
@evert-smit distanceFromWindow won't work in this case, even if I add the header inside the ScrollView. The solution was to do something similar to what @Komeyl94 did. It's not nice, and it's a hack, and you have to know the height of the header beforehand
I also had this issue and my solution was to add the header as the first item of the list and gave it its own ViewType, and it worked perfectly.
you can make example ?
I had to solve this problem as well so I thought it would be worth putting an annotated example of how you can do this in a way that is compatible will what RecyclerListView expects.
If you replace Header with whatever component you need you'll get a RecyclerListView with that header that exists within the confines of the list
import PropTypes from 'prop-types';
import React from 'react';
import { RecyclerListView } from 'recyclerlistview';
import { ScrollView, StyleSheet, View } from 'react-native';
// Create header component with gap
const style = StyleSheet.create({
header: {
marginTop: 16,
},
});
const Header = () => <View style={style.header} />;
// Use forwardRef to wrap a ScrollView that has the Header before the rest of the
// Children are rendered. forwardRef is there because RecyclerListView expects whatever you
// pass it as the externalScrollView to have the same methods available as ScrollView
const ScrollViewWithHeader = React.forwardRef(({ children, ...props }, ref) => {
return <ScrollView
ref={ref}
{...props}
>
<Header />
{children}
</ScrollView>;
});
// Overriding PropType because it doesn't expect a forwardRef response even though that
// works without issue
RecyclerListView.propTypes.externalScrollView = PropTypes.object;
// Use the headered scroll view to underly the RecyclerList
const RecyclerListViewWithHeader = (props) => {
return <RecyclerListView
externalScrollView={ScrollViewWithHeader}
{...props} />;
};
export default RecyclerListViewWithHeader;
I had to solve this problem as well so I thought it would be worth putting an annotated example of how you can do this in a way that is compatible will what RecyclerListView expects.
If you replace Header with whatever component you need you'll get a RecyclerListView with that header that exists within the confines of the list
import PropTypes from 'prop-types'; import React from 'react'; import { RecyclerListView } from 'recyclerlistview'; import { ScrollView, StyleSheet, View } from 'react-native'; // Create header component with gap const style = StyleSheet.create({ header: { marginTop: 16, }, }); const Header = () => <View style={style.header} />; // Use forwardRef to wrap a ScrollView that has the Header before the rest of the // Children are rendered. forwardRef is there because RecyclerListView expects whatever you // pass it as the externalScrollView to have the same methods available as ScrollView const ScrollViewWithHeader = React.forwardRef(({ children, ...props }, ref) => { return <ScrollView ref={ref} {...props} > <Header /> {children} </ScrollView>; }); // Overriding PropType because it doesn't expect a forwardRef response even though that // works without issue RecyclerListView.propTypes.externalScrollView = PropTypes.object; // Use the headered scroll view to underly the RecyclerList const RecyclerListViewWithHeader = (props) => { return <RecyclerListView externalScrollView={ScrollViewWithHeader} {...props} />; }; export default RecyclerListViewWithHeader;
I tried you implementation and it works ok, but I noticed one problem, by scrolling down rows rendering ok, and by scrolling up row is rendered when I reach half of the row, so I can see white row until I reach half of it. Then it is populated. When I remove Header it works normally. Do you have solution for this?
This is how Recycle view looks like :
style={{paddingTop: 8, paddingBottom: 60}}
contentContainerStyle={{
alignSelf: 'center',
}}
externalScrollView={this.externalScrollView}
forceNonDeterministicRendering={true}
canChangeSize={true}
dataProvider={this.state.dataProvider}
rowRenderer={this.rowRenderer}
layoutProvider={this.layoutProvider}
refreshing={true}
onRefresh={this.onRefresh}
keyExtractor={item => item.id.toString()}
onScroll={this._scrolled.bind(this)}
extendedState={this.state}
onVisibleIndexesChanged={this.onViewableItemsChanged}
onEndReached={this.onEndReached}
onEndReachedThreshold={0.9}
viewabilityConfig={this.viewabilityConfig}
snapToAlignment={"center"}
/>
@naqvitalha @evert-smit distanceFromWindow would only be a requirement for web, right? Or would this be necessary in a react-native project?
I've implemented a header using the external scrollview approach, however I get some weird scrolling behaviour/jumping in the list when we've scrolled near the end of it
The issue is still present, haven't found a decent example how to implement a header. Passing it as a first item doesn't help, because if I have an action in header which triggers new data load, header also gets rerendered which is annoying. By the way, passing my own header also rerenders the header.
The issue is still present, haven't found a decent example how to implement a header. Passing it as a first item doesn't help, because if I have an action in header which triggers new data load, header also gets rerendered which is annoying. By the way, passing my own header also rerenders the header.
I actually have the opposite issue, because the header doesn't consistently re-render, sometimes it doesn't re-render when I need it to when the state has updated. So sometimes when I click a dropdown menu I've implemented in the header, the dropdown menu doesn't re-render to reflect the selected option, even if the list rows do get updated...
@abduraufsherkulov @chanmathewrbc I had the same problems with re-rendering the header both when I didn't want and also not re-rendering when I want.
What I did to fix my issues:
1) Use the custom ScrollView listed above, but wrap it in useMemo. Give it a dependency array if you need to control when it should re-render or not. Note: This is only for the outer ScrollView component, not for the header
2) If you do the above with an empty dependency array, your header won't re-render at all. Your first instinct might be to add your header component to the dependency array of the above useMemo. If you do that, your header will re-render, but so will your entire scroll view and you will always be scrolled instantly to the top.
3) Fortunately, you can pass props with scrollViewProps. These props bypass the useMemo and will always be updated, regardless of the scroll view.
So for example
const ScrollViewWithHeader = React.useMemo(() => React.forwardRef(({ children, ...props }, ref) => {
return <ScrollView
ref={ref}
{...props}
>
{props.headerComponent}
{children}
</ScrollView>;
}), []); // Pass dependencies for ScrollView here
const RecyclerListViewWithHeader = (props) => {
return <RecyclerListView
externalScrollView={ScrollViewWithHeader}
scrollViewProps={
{ headerComponent: Header }
}
{...props} />;
};
Now, your ScrollView is completely controllable and will re-render only when you want to. At the same time, your headerComponent can be controlled separately. If you want to control when it re-renders, you can wrap it in its own useMemo.
This fixed my scenario where I needed to have a custom header component that only re-rendered certain times, while leaving the rest of my list intact. I gave my ScrollView an empty dependency array and just re-rendered my headerComponent.
Hope this helps!
{...props} />;
Thank you for sharing. Can you please also share how your layoutProvider looks and how you accounted for header height?
Does this support multiple headers in between data?
@ehsan6sha Header height should be automatically considered since it is rendered on top of the list-view. If you only have a header and then your list items, regardless of the header height, it should work.
As for multiple headers in between data, it probably will need more customisation. I haven't had this use case so I am not sure.
My layout provider is pretty basic
const [layoutProvider] = useState(
new LayoutProvider(
() => {
return 0;
},
(type, dim) => {
dim.width = Dimensions.get("screen").width;
dim.height = estimatedLayoutHeight; // Comes from parent or defaults to 80
}
)
);
@dariuscosden the error scrolling up and down with half white cell item is persist.
@ilicnenad have you found the solution yet?
Most helpful comment
I had to solve this problem as well so I thought it would be worth putting an annotated example of how you can do this in a way that is compatible will what RecyclerListView expects.
If you replace Header with whatever component you need you'll get a RecyclerListView with that header that exists within the confines of the list