React Native - SectionList scroll to end when list is loaded first time - react-native

I am working on a chat application.
I am using SectionList to render messages.
I want to scroll to bottom when list loaded.
All messages are of dynamic size so there is no possibility to use getItemLayout.
If I call scrollToLocation immediately, SectionList throws error that scrollToLocation should be used in conjunction with getItemLayout.
How can I achieve this?
loadNextPage() {
var dataSource = this.message_list.load_next_page()
this.setState({ data : dataSource,
isLoading:false,
isPullToRefreshing:false})
}
onMessagesReceived() {
this.loadNextPage()
this.scrollToEnd()
}
scrollToEnd(animated) {
var length = this.state.data.length
if(length > 0) {
setTimeout(()=> {
this.refs.chatList.scrollToLocation({animated:animated,
sectionIndex:length-1,
itemIndex:this.state.
data[length-1].data.length-1,
viewPosition:1})
}, 10)
}
}

Related

how to count views in react native flatlist items

I have a flat list and it contains a list of posts and videos, now I need to count the views for each item, I tried using viewabilityConfig and onViewableItemsChanged in the flat list and it works fine if I only want to count visible item as a view.
problem is that I had a function written inside onViewableItemsChanged for pausing video right after user scrolls from video.
now I have two functions with different execution times. how can I handle these two functions ?
onViewableItemsChanged = (info) => {
if (this.props.enableView) {
console.log('item viewed after minimumViewavlitytime 5s: ', info) //done after 5s of view time
}
this.pauseVideoFunction(); //done exactly after scrolling
};
viewabilityConfig = {
minimumViewTime:5000,
waitForInteraction: false,
viewAreaCoveragePercentThreshold: 95,
}
i handled it this way:
removed minimumViewTime from config file
added this to constructor since its a class component
this.debouncedViewCount = debounce(this.countView, 5000);*
called debouncedViewCount inside onViewableItemsChanged
now I have a denounced view counter which runs after 5 second if scroll doesn't move and can pause video right after scrolling
class ComponentA extends React.Component {
constructor(props) {
super(props);
this.debouncedViewCount = debounce(this.countView, 5000); //aaaaaaaaaaa
}
countView = (info) => {
debounce(console.log('Debouced 5s', info), 5000, false)
}
onVideoExitScreen = (info) => { //todo working on view aaaaaaaaaaaaaaaaaaaa
if (this.props.enableView) {
this.debouncedViewCount(info);
}
pauseVideoFunction();
};
}
//inside Flatlist
onViewableItemsChanged={this.onViewableItemsChanged}

Fix ScrollView on small scroll, React Native

I have a ScrollView and I want it to scroll only to top and bottom, I have done it depending on it's position when onTouchEnd happens. But I need to scroll to certain point where it scrolls to bottom or top. Can I do the same when I do small and sharp scroll?
here's my code
const [scrollToTop, setScrollToTop] = useState(false)
const scrollViewRef = useRef()
const handleGestureEvent = (e) => {
if (e?.nativeEvent?.contentOffset?.y > 120) {
setScrollToTop(false)
} else {
setScrollToTop(true)
}
}
const fixScrollView = () => {
if (scrollViewRef && scrollViewRef.current) {
if (scrollToTop) {
scrollViewRef.current.scrollTo({y: 0, animated: true})
} else {
scrollViewRef.current.scrollToEnd({animated: true})
}
}
}
<ScrollView
ref={scrollViewRef}
onScroll={handleGestureEvent}
onTouchEnd={fixScrollView}></ScrollView>
But now I need to scroll to certain point on the screen where it scrolls to top or bottom. I don't know, is there a way to determine small and sharp scroll so that the list would scroll to bottom or top when it occurs
You can run the "scrollTo" method of the ScrollView. Check out the source.
You can get reference the ScrollView component by setting ref property and using this.refs as described here: https://facebook.github.io/react/docs/more-about-refs.html

How to attach an event handler to a dynamically created item in React native

I have created the component array using the list.push, now each item in a view has a button, now the problem is when there is a more components (50-100), when I click to that button, the app hangs for a while.
I have tried the following :
if(!Object.prototype.hasOwnProperty.call(this.clickHandlers, index))
{
this.clickHandlers[index] = () =>
this.props.btnCheckOutAction(this.props.currentState.isModelOpen);
}
return this.clickHandlers[index];
handleItemClick(key) {
debugger
console.log("index::::::::::::::::::::::", key);
if (!Object.prototype.hasOwnProperty.call(this.clickHandlers, index)) {
this.clickHandlers[index] = () => this.props.btnCheckOutAction(this.props.currentState.isModelOpen);
}
return this.clickHandlers[index];
}
When I click to the button of the individual the render call the number of times the list has a component.

React Native : How can I get gestures onTouchStart, onTouchEnd and onTouchMove event positions ( like X and Y Coordinations) in Android

How would I get the coordinates of my onTouchEnd event. So I touch or move figure anywhere within the display and I can retrieve the X, Y positioning of where it happened. onResponderRelease is not triggered in a onTouchEnd on Android,
I've included example implementations for all of the gesture response event handlers, but have commented out most of them on my View to just provide the basic functionality: subscribing to all touch and move events
pseudo code would look like this :
<View
style={this.props.style}
ref={this._saveRef}
onStartShouldSetResponder={(event) => {
return this.handleDoubleTap({nativeEvent:
event.nativeEvent});
}}
onResponderGrant={this._onTouchStart}
onResponderMove={this._onTouchMove}
onResponderRelease={this._onTouchEnd}
onResponderTerminate={this._onTouchEnd} // When
onResponderRelease can't call by some reason
>
{this.props.children}
</View>
Responder Event Handler Methods :
if (this.isDoubleTap) {
return false;
}
this.context.setScroll && this.context.setScroll(false);
const currentTouchTimeStamp = Date.now();
this._prevTouchInfo = {
prevTouchX: nativeEvent.pageX,
prevTouchY: nativeEvent.pageY,
prevTouchTimeStamp: currentTouchTimeStamp
};
this.props.onStart(nativeEvent);
};
_onTouchMove = ({nativeEvent}) => {
if (nativeEvent.touches.length <= 1) {
if (this.isDoubleTap) {
return false;
}
const self = this;
const gesture = {
x0: this._prevTouchInfo.prevTouchX,
x1: nativeEvent.pageX,
y0: this._prevTouchInfo.prevTouchY,
y1: nativeEvent.pageY,
dx: nativeEvent.pageX - this._prevTouchInfo.prevTouchX,
dy: nativeEvent.pageY - this._prevTouchInfo.prevTouchY
};
InteractionManager.runAfterInteractions(function () {
self.props.onMove(nativeEvent, gesture);
});
}
};
_onTouchEnd = ({nativeEvent}) => {
nativeEvent.touches.length === 0 && this.context.setScroll && this.context.setScroll(true);
this.props.onEnd(nativeEvent);
};
}
I am Developing an Application in React native.Actually when i am touch on Particular Position On view, getting the Corresponding x and y co-ordinates. App UI would look like this:
If this is still not enough functionality for android (eg. if you need multi-touch info), refer to PanResponde

Transition with keepScrollPosition and navigateBack

We are using Durandal for our SPA application and came to a, in my opinion, common use case. We have two pages: one page is a list of entities (with filters, sorting, virtual scroll) and another is detail preview of an entity. So, user is on list page and set a filter and a list of results comes out. After scrolling a little bit down user notice an entity which he/she would like to see details for. So clicking on a proper link user is navigated to details preview page.
After "work finished" on preview page user click back button (in app itself or browser) and he/she is back on the list page. However, default 'entrance' transition scroll the page to the top and not to the position on list where user pressed preview. So in order to 'read' list further user have to scroll down where he/she was before pressing preview.
So I started to create new transition which will for certain pages (like list-search pages) keep the scroll position and for other pages (like preview or edit pages) scroll to top on transition complete. And this was easy to do however, I was surprised when I noticed that there are strange behavior on preview pages when I hit navigateBack 'button'. My already long story short, after investigation I found out that windows.history.back is completing earlier then the transition is made and this cause that preview pages are scrolled automatically down to position of previous (list) page when back button is hit. This scrolling have a very unpleasant effect on UI not mentioning that it is 'total catastrophe' for my transition.
Any idea or suggestion what could I do in this case?
Here is the code of transition. It is just a working copy not finished yet as far as I have this problem.
define(['../system'], function (system) {
var fadeOutDuration = 100;
var scrollPositions = new Array();
var getScrollObjectFor = function (node) {
var elemObjs = scrollPositions.filter(function (ele) {
return ele.element === node;
});
if (elemObjs.length > 0)
return elemObjs[0];
else
return null;
};
var addScrollPositionFor = function (node) {
var elemObj = getScrollObjectFor(node);
if (elemObj) {
elemObj.scrollPosition = $(document).scrollTop();
}
else {
scrollPositions.push({element: node, scrollPosition: $(document).scrollTop()});
}
};
var scrollTransition = function (parent, newChild, settings) {
return system.defer(function (dfd) {
function endTransition() {
dfd.resolve();
}
function scrollIfNeeded() {
var elemObj = getScrollObjectFor(newChild);
if (elemObj)
{
$(document).scrollTop(elemObj.scrollPosition);
}
else {
$(document).scrollTop(0);
}
}
if (!newChild) {
if (settings.activeView) {
addScrollPositionFor(settings.activeView);
$(settings.activeView).fadeOut(fadeOutDuration, function () {
if (!settings.cacheViews) {
ko.virtualElements.emptyNode(parent);
}
endTransition();
});
} else {
if (!settings.cacheViews) {
ko.virtualElements.emptyNode(parent);
}
endTransition();
}
} else {
var $previousView = $(settings.activeView);
var duration = settings.duration || 500;
var fadeOnly = !!settings.fadeOnly;
function startTransition() {
if (settings.cacheViews) {
if (settings.composingNewView) {
ko.virtualElements.prepend(parent, newChild);
}
} else {
ko.virtualElements.emptyNode(parent);
ko.virtualElements.prepend(parent, newChild);
}
var startValues = {
marginLeft: fadeOnly ? '0' : '20px',
marginRight: fadeOnly ? '0' : '-20px',
opacity: 0,
display: 'block'
};
var endValues = {
marginRight: 0,
marginLeft: 0,
opacity: 1
};
$(newChild).css(startValues);
var animateOptions = {
duration: duration,
easing : 'swing',
complete: endTransition,
done: scrollIfNeeded
};
$(newChild).animate(endValues, animateOptions);
}
if ($previousView.length) {
addScrollPositionFor(settings.activeView);
$previousView.fadeOut(fadeOutDuration, startTransition);
} else {
startTransition();
}
}
}).promise();
};
return scrollTransition;
});
A simpler approach could be to store the scroll position when the module deactivates and restore the scroll on viewAttached.
You could store the positions in some global app variable:
app.scrollPositions = app.scrollPositions || {};
app.scrollPositions[system.getModuleId(this)] = theCurrentScrollPosition;