React - scrolling to the bottom of a flex container without scrolling the whole page - reactjs

I'm using react and trying to build a facebook like chat - where the right column scrolls down and has a list of threads, and the middle column had the thread content and scrolls bottom up.
I have it all setup using flex, but am stuck on getting the middle column to scroll to the bottom by default (since I want to see the latest chat message). Here is the snippet for the actual container (I'm using React bootstrap):
<Row >
<Col ref={ (node) => { this.cont = node }}>
{this._buildThread()};
</Col>
</Row>
and here is the snippet for my ComponentDidMount:
componentDidMount() {
MessageStore.addChangeListener(this._onChange);
MessageService.getThread(this.props.id, 1000, 1, false);
this.cont.scrollTop = this.cont.scrollHeight;
}
Where cont is the ref for the container div that holds my messages. Yet nothing happens - it doesn't scroll, and if I look at node.scrollTop after setting it, it remains at 0 - seems immutable.
Any help would be much appreciated. Thanks!

Not sure about the details of your CSS, but I just had a similar-looking issue. The solution in my case was to make sure that the element itself would scroll and not its container:
.containerIWantToScroll {
overflow-y: auto;
}
If the element's container expands to fit the element, then that element will not scroll, thus its scrollTop value is always zero.

When are you triggering the code you included? To scroll to the bottom on render, I would write something like the following, calling triggerScroll in componentDidMount:
class App extends React.Component {
componentDidMount () {
this.triggerScroll();
}
triggerScroll () {
this.cont.scrollTop = this.cont.scrollHeight;
}
render () {
return (
<div>
<div className='containerIWantToScroll' ref={ (node) => { this.cont = node } }>
Content
</div>
</div>
)
}
}

Related

Printing multiple pages not working in React-to-Print

we are loading 100 records in the grid initially, after that when we scroll down , loading another set of data.
But when we try to print the page its loading just 100 records in the print preview screen, we are using react-to-print, functional component.
const handlePrint = useReactToPrint({
content: () => componentRef.current,
});
we want load all the data available in the List, please help.
I found myself in a similar problem and this answer from github helped me.
You have to surround your render with a div of styling overflow: scroll and also remove overflow styling, maximum height and any other view limiting styling from the surrounded element's "stylings" like the example below.
Disclaimer: I used MUI in some instances.
render() {
<div sx={{ overflow: "scroll" }}>
{
sampleIterable.map((x,n) => {
...
})
}
</div>
}

Recording user interactions in React Native / Snap Carousel - firstItem

I have been having this issue for a couple of days and cannot seem to find a solution.
I would like to record the user's interaction on a database. I am displaying data using a React Native Snap Carousel, and need to record if the item was viewed by the user. To do this, and as described in other stack overflow questions, I am using "onSnapToItem". onSnapToItem is triggered everytime I change from one slide to another. Then, if the user views the slide for more than 2 seconds, I count that as an interaction.
The problem is that onSnapToItem is not triggered on the firstItem (which makes sense, because I am not changing slide). Can anybody think of a solution?
<Carousel
vertical
layout={"default"}
ref={_carousel}
data={promos}
renderItem={_renderItem}
autoplay={true}
autoplayInterval={4000}
onSnapToItem = { (index) => {
clearTimeout(writeDelay)
writeDelay=setTimeout (()=>{
console.log(promos[index].id)
},2000)
}}
onEndReached={_retrieveMore}
/>
Programmatically snap to item on screen focus.
componentDidMount() {
setTimeout(() => this._carousel.snapToItem(0), 1000);
// if above code doesn't work then you can try is for first item
writeDelay=setTimeout (()=>{
console.log(promos[0].id); // index here is 0 for first item
},1000)
}
...
render() {
<Carousel
...
ref={c => { this._carousel = c }}
/>
}`
you can use this library #svanboxel/visibility-sensor-react-native

Intersection Observer loses entries

I am trying to use IntersectionObserver in my react app. I want to achieve lazy loading with it. Therefore I observe several elements and if they appear on screen I load content inside them.
Here is a very simplified code:
class Table extends React.Component {
constructor() {
super()
this.state = {
entries: 0
}
}
componentWillUnmount() {
console.log('componentWillUnmount')
if (this.observer) this.observer.disconnect()
}
observe (c) {
if (!this.observer) {
this.observer = new IntersectionObserver(
entries => {
this.setState({entries: entries.length})
},
{ threshold: [0, 0.25, 0.5, 0.75, 1] }
)
}
if (!c) return
this.observer.observe(c)
}
render() {
const {entries} = this.state
return (
<div>
<h1>Number of observer entries: {entries}</h1>
<div
ref={this.observe.bind(this)}
style={{height: '1000px', display: 'block', width: '500px'}}
/>
<div
ref={this.observe.bind(this)}
style={{height: '1000px', display: 'block', width: '500px'}}
/>
</div>
)
}
}
ReactDOM.render(<Table />, document.querySelector("#app"))
When a component is mounted it shows two elements are observed but as soon as I scroll down it changes to only one element. I have not idea what I am missing.
JSON fiddle - https://jsfiddle.net/w1zn49q6/12/
The divs are stacked one after the other vertically. In the initial render, as they are laid out, the intersection observer gets triggered for both, as they enter the viewport together (the first div enters, the second div exits). However, once they are rendered, they will enter/exit one at a time on a normal course of vertical scrolling, hence the entries will only ever contain one div, which intersected the x-axis most recently.
The intersection entry only reports a transition to/from 0 (not in view) from/to 1 (fully in view). So when one div has fully exited/entered the view, it will no longer be present in an entry update.
You can still get 2 entries however :). If you manage to scroll really fast! Try it using an accelerated mouse wheel. So basically, between two intersection calculations, if both the divs moved too far, both will raise the intersection event, but if they move slowly, the intersection will be gradual because they are stacked one by one.
If you would stack them in the same row, you will continuously get two entries, as both will be intersecting at the same moment with the x-axis.

Tabbing to First Element when react-virtualized is scrolled to the bottom

I am using react-virtualized library. And I need to be able to tab through the list.
Since not all the items are rendered, I am not able to tab to the last row
Similar issue will happen if I want to tab to the first row when virtual list is scrolled to the bottom of the list
If I was able to always render first row and last row, no matter scrolling position, I would be able to tab to the last and first element.
Use case:
Imagine current focus is on input box which is situated above the virtual list, and imagine virtual list is scrolled to the bottom.
If I press on my keyboard tab I expect my focus to be on the first element. But because the first element is not rendered,the focus will be on the first rendered row by virtual list. I hope this makes sense.
This is my current code:
class Locations extends React.Component {
setListElementRef = (ref) => {
this.listRef = ref;
}
rowRenderer = ({ index, style }) => {
return (
<div style={style} key={index} >
<CheckBoxContainer />
</div>
);
}
render() {
const {
filteredLocations, onSearchValueChange,
} = this.props;
return (
<List
tabIndex={-1}
isScrollingOptOut
ref={this.setListElementRef}
rowRenderer={this.rowRenderer}
rowCount={filteredLocations.length}
rowHeight={32}
height={300}
width={218}
overscanRowCount={5}
/>
);
}
}

Using SwipeableViews with updateHeight()

A new update to SwipeableViews allows for each Tab in the MaterialUI Tabs Component to have their own height. Let me back up a little. Before, MaterialUI Tabs component will show all tabs in the height of the longest tab. This created unnecessary scrolling on tabs that didn't need it. SwipeableViews recently fixed this by adding this to their component
<SwipeableViews
action={actions => {this.swipeableActions = actions;}}>
<div>{'slide n°1'}</div>
<div>{'slide n°2'}</div>
<div>{'slide n°3'}</div>
</SwipeableViews>
and
componentDidUpdate() {
this.swipeableActions.updateHeight();
}
That fixed the height issue across tabs on their loaded state. But when items are hidden and shown with a click event, the height persists and shown items do not show (being cut off from view)
See image (imgur failed to load the image) see the image link instead
image
You need to add:
animateHeight
to the SwipeableViews component.
You may also have to pass a function down to child components in order to update the height when a child modifies the view.
UpdateSwipeableViewHeight = () => {
if (this.swipeableActions) this.swipeableActions.updateHeight()
};
<SwipeableViews
id="searchTabsSwipeableView"
axis={theme.direction === 'rtl' ? 'x-reverse' : 'x'}
index={activeTabIndex}
onChangeIndex={index => {
this.handleChange(null, index);
this.UpdateSwipeableViewHeight();
}
}
action={actions => (this.swipeableActions = actions)}
animateHeight
>
<div className={classes.swipableViewsComponent}>
<ChildComponent updateHeight={this.UpdateSwipeableViewHeight} />
</div>

Resources