react-infinite-scroll-component resets scroll position - reactjs

I'm using react-infinite-scroll-component with Mantine's Select component, and have come across an issue. Basically whenever new data loads, the scroll bar resets all the way to the top, which as you can imagine is quite annoying in terms of UX.
Does anyone have a clue of why this is happening? While inspecting the DOM, it seems that the infinite scroll component completely re-renders for some reason.
You can see what I mean in this gif:

I'm almost sure that you are using some state for checking. I will give you a real bad example & solution for it.
Bad example which isn't works:
const hasData = !isLoading && postsResp?.content?.length;
{!hasData && (
<InfiniteScroll
dataLength={postsResp?.content?.length}
next={() => getContentFeed(true)}
hasMore={!postsResp?.last}
loader={(
<Skeleton
variant='text'
width={120}
height={24}
sx={{ margin: 'auto' }}
/>
)}
>
{postsResp?.content?.map((post: Content) => (
<Post key={`post_${post.uuid}+${Math.random()}`} post={post} />
))}
</InfiniteScroll>
) : (
'No posts'
)}
And this is a one which works good:
I know that you noticed i've moved the check if hasData on other place. I did that because each time this state is updating with new value and react re-render my component. That's why the scroll goes on top each time.
{(!hasData && !isLoading) && (
'No posts'
)}
<InfiniteScroll
dataLength={postsResp?.content?.length ?? 0}
next={() => getContentFeed(true)}
hasMore={!postsResp?.last}
loader={(
<Skeleton
variant='text'
width={120}
height={24}
sx={{ margin: 'auto' }}
/>
)}
>
{postsResp?.content?.map((post: Content) => (
<Post key={`post_${post.uuid}+${Math.random()}`} post={post} />
))}
</InfiniteScroll>
The main idea here is to remember to do not update any state according to your infinity scroll which can re-render your component.

Related

How do I tap only once on touchable opacity placed inside a scrollview?

I already researched similar questions on the topic, so please don't be in a hurry to close this question.
I have a search screen, where I type a search term and I fetch listed results. Once fetched, I want to tap on any one of them and navigate to that page details object. My initial first tap is never been detected, but my second is. My component:
return (
<View>
<TextInput
value={query}
placeholder="Type Here..."
onChangeText={(search) => setQuery(search)}
/>
{fetching ? (
<Spinner
visible={fetching}
textContent={'Fetching data...'}
textStyle={{ fontSize: 14 }}
/>
) : null}
{items ? (
<ScrollView keyboardShouldPersistTaps={true}>
<FlatList
numColumns={1}
horizontal={false}
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() =>
navigation.navigate('Item Details', { id: item.id })
}>
<ItemDisplay item={item} keyboardShouldPersistTaps="always" />
</TouchableOpacity>
)}
/>
</ScrollView>
) : null}
</View>
);
My ItemDisplay is just a row of item's photo and item's id. Nothing fancy.
As suggested on the similar questions, I do use <ScrollView keyboardShouldPersistTaps={true}> and <ItemDisplay item={item} keyboardShouldPersistTaps="always" /> However it doesn't help m, I still need to tap twice. Any ideas on how do I fix this? I am testing it on android, if that matters.
I also tried <ScrollView keyboardDismissMode="on-drag" keyboardShouldPersistTaps={"always"} > but this doesnt help as well.
If I understood your problem correctly, you are facing an issue clicking an item when the Keyboard is present.
If so you can simply pass keyboardShouldPersistTaps as always to your FlatList and then the children of the list will receive taps when an item is pressed.
Also, you shouldn't be using a ScrollView to wrap your Flatlist, if they are both having the same orientation, numColumns isn't required as you have passed it as 1
You can read more about keyboardShouldPersistTaps at https://reactnative.dev/docs/0.64/scrollview#keyboardshouldpersisttaps
Take a look at this Live Snack to see it in action.

How to make the Collapse position to bottom-right after clicking on expand icon in AntD

I am using AntD Collapse for displaying a list of items after expand icon is clicked.
I want the position of expandIcon to go to bottom-right after all the list of the data when expand icon is clicked (just like in google news), but found only two options (left|right) for 'expandIconPosition', no option for top or bottom.
How can we align the expandIcon to bottom-right, when expand icon is clicked?
Few lines from the code for reference:
<Collapse
ghost
style={{ marginTop: "-1vh" }}
expandIcon={({ isActive }) => (
<DownOutlined
style={{ marginTop: "-2vh" }}
rotate={isActive ? 180 : 0}
/>
)}
expandIconPosition="right"
>
<Panel>
<div>
{list()} //list of items
</div>
</Panel>
</Collapse>
Here's one possible solution. Make Collapse a controlled component by specifying activeKey prop and then the value of it will be based on state. Then, by tracking the activeKeys state you can now do a conditional rendering (hide and show) on icons:
const [activePanelKeys, setActivePanelKeys] = useState([]);
const handlePanelIconClick = (panelKey, makeActive) => {
if (makeActive) {
setActivePanelKeys([...activePanelKeys, panelKey]);
} else {
setActivePanelKeys(activePanelKeys.filter((aPK) => aPK !== panelKey));
}
};
<Collapse
activeKey={activePanelKeys}
expandIconPosition="right"
expandIcon={() => <DownOutlined />}
// expandIcon={(panelProps) => (
// <DownOutlined
// onClick={() => handlePanelIconClick(panelProps.panelKey, true)}
// />
// )}
onChange={e => setActivePanelKeys(e)} //if you want to click only icon, comment this and uncomment the above expandedIcon
>
<Panel
header="This is panel header 1"
key="p1"
showArrow={activePanelKeys.indexOf("p1") === -1}
>
<div>{text}</div>
{activePanelKeys.indexOf("p1") !== -1 && (
<div style={{ textAlign: "right", marginTop: 10 }}>
<UpOutlined onClick={() => handlePanelIconClick("p1", false)} />
</div>
)}
</Panel>
{/* <PanelContent header="This is panel header 2" key="p2">
{text}
</PanelContent> */}
</Collapse>;
Here is the complete sample code:
Note: I tried to make a reusable Panel component but it seems that the reveal animation were gone. You can uncomment the commented PanelContent on the code to see the difference.
Hope that I hit what you want.

Unable to solve the Menu bar issue using reactjs?

I'm new to Framework and i have an issue with me sidebar which is showing Table tab as default but it should not show any tab's page as clicked. By default it should show nothing. When we click on a particular tab then it should show color on the tab of the sidebar. If i give -1 then the color is getting disappeared on the menu tab but the Tab's page remain as it is. Is it possible to give pathname. how to fix by giving id ? . If Yes Can anyone help me in fixing this issue?
Here is the code:
<Drawer
className={classes.drawer}
variant="permanent"
classes={{
paper: classes.drawerPaper
}}
>
<div className={classes.toolbar} />
<List>
{["table", "home"].map((item, index) => {
const Icon = itemsConfig[item].icon;
return (
<ListItem
component={Link}
to={itemsConfig[item].link}
selected={selectedIndex === index}
onClick={event => handleListItemClick(event, index)}
button
key={item}
>
<ListItemIcon>
<Icon />
</ListItemIcon>
<ListItemText primary={itemsConfig[item].text} />
</ListItem>
);
})}
</List>
</Drawer>
Here is whole code: "https://codesandbox.io/s/fervent-browser-ecz7z"
Can anyone help me in this ?
Define your state, that holds the selected index in your <Layout /> component. Define function that manipulate that state and pass it to , <Home /> andcomponents along with the newly defined state. Finally call the function after the initial rendering of the component. For the (class component) use:
componentDidMount() {
this.props.setTabIndex(this.props.index);
}
For the <Table /> use:
useEffect(() => {
props.setTabIndex(props.index);
}, []);
The full result is posted here: https://codesandbox.io/s/cold-mountain-hlhk8?

Google map does not shrink when user opens sidebar

I am new to both React and Grommet and have worked through the getting started tutorial. When I add a google map to the main container it renders and fills the whole screen. However, when I click the notification icon to load the sidebar it only renders in a tiny column on the side. Is there a way for me to edit the style to adjust the width automatically or do I need to have the container width as a property that changes when the button is clicked? (Please excuse any question format errors as this is my first post.)
I have tried looking through the Grommet box props and various CSS style settings without success.
App.js:
const { showSidebar } = this.state;
return (
<Grommet theme={theme} full>
<ResponsiveContext.Consumer>
{size => (
...
<Box direction='row' flex overflow={{ horizontal: 'hidden' }}>
<Box
align='start'
justify='start'
fill='horizontal'
responsive='true'
>
<MapContainer />
</Box>
{!showSidebar || size !== 'small' ? (
<Collapsible direction='horizontal' open={showSidebar}>
<Box
flex
width='medium'
background='light-2'
elevation='small'
align='center'
justify='center'
>
sidebar
</Box>
</Collapsible>
) : (
...
}
</Box>
</Box>
)}
</ResponsiveContext.Consumer>
</Grommet>
);
MapContainer.js:
return (
<Map google={this.props.google} zoom={14}/>
);
I would like the map to shrink to fill the container/the container to shrink when the sidebar loads.

React native toggle component without state

I just want to toggle SearchButton and SearchIcon , I use following code
searchBarButton() {
Actions.refresh();
this.setState({ showSearchBar: !this.state.showSearchBar });
}
render() {
if (this.state.showSearchBar) {
return (
<Header>
<View style={styles.searchHolder}>
<Item style={styles.searchBar}>
<Icon name="ios-search" />
<Input placeholder="Search" />
</Item>
<Button style={styles.searchButton} onPress={this.searchBarButton}>
<Text>Search</Text>
</Button>
</View>
</Header>
);
}
return (
<Header>
<Button onPress={() => this.searchBarButton()} transparent>
<Icon name="search" style={styles.bigblue} />
</Button>
</Header>
);
}
So basically It is very fast initially , But when my scene contains lots of items in flat List , There is like 1 to 2 seconds delay between toggle.I guess its due to re-rendering all items in page.
So How can I toggle this in more easier and efficient way without Re rendering whole page without using state
I think you have to hide one of them by using style prop.
Just render both first, toggle one of them to be hidden(by using state),
<Header>
<View style={[this.state.showSearchBar && styles.hidden]}>Button</View>
<View style=[{!this.state.showSearchBar && styles.hidden}]>Icon</View>
</Header>
do not remove them from virtual dom(I don't know what to call it in mobile) completely
I'd recommend you to grab some UI library. Take a look at this: https://react-bootstrap.github.io/components.html#utilities
You can just trow you your searchbar code in this
<Collapse in={this.state.showSearchBar}>
...
</Collapse>
And everything is taken care of very efficiently.
You just need to trow your button in an operator {!showSearhBar && <Component/>}
Also use react dev tools to check what re-renders

Resources