This is how it works depending on the source code:
const {contentLength, visibleLength, offset} = this._scrollMetrics;
const distanceFromEnd = contentLength - visibleLength - offset;
if (
onEndReached &&
this.state.last === getItemCount(data) - 1 &&
distanceFromEnd < onEndReachedThreshold * visibleLength &&
(this._hasDataChangedSinceEndReached ||
this._scrollMetrics.contentLength !== this._sentEndForContentLength)
) {
// Only call onEndReached once for a given dataset + content length.
this._hasDataChangedSinceEndReached = false;
this._sentEndForContentLength = this._scrollMetrics.contentLength;
onEndReached({distanceFromEnd});
}
So, first of all it chek for onEndReached callback if exists, after that it check if the last element of data is rendered(not necessarily visible), and only then it check if you scroll enough to the bottom of the list.
visibleLength here is the height of your list element (if horizontal isn't set), and contentLength is the height of your list element container multiply number of elements in the data.
As you can see onEndReachedThreshold is reverce number of "visible screens" (i.e heights of your list element) you should scroll till your onEndReached callback will fire - bigger onEndReachedThreshold, less you should scroll. With the value onEndReachedThreshold = 0.5, your callback will fire almost at the "end" of the list. But remember that it will not fire till the last element is rendered, no matter what value you will set.