PageView's onPageScroll gives wrong position on iPhone 12+ - react-native

Using "react-native-pager-view": "^6.1.2" package in ReactNative.
I have the same bug as here.
https://github.com/callstack/react-native-pager-view/issues/503
I'm trying PagerView with the code pasted below, but when I swipe to move the page, e.nativeEvent.position is different from the actual page index.
import PagerView, { PagerViewOnPageScrollEvent, PagerViewOnPageSelectedEvent } from 'react-native-pager-view';
import Modal from 'react-native-modal';
const onPageScroll = useCallback((e: PagerViewOnPageScrollEvent) => onPageScrollPagerView(e, setCurrentTabIndex), []);
const onPageScrollPagerView = (
e: PagerViewOnPageScrollEvent,
setCurrentTabIndex: React.Dispatch<React.SetStateAction<number>>,
) => {
console.log(`onPageScroll position = ${e.nativeEvent.position}`);
setCurrentTabIndex(e.nativeEvent.position);
};
return (
<Modal isVisible={isVisible}>
<SafeAreaView>
<View>
<PagerView ref={viewPager} initialPage={0} onPageScroll={onPageScroll} onPageSelected={onPageSelected}>
      {list.map((item) => (
     <View key={item.id}>
    {item.content}
     </View>
      ))}
        </PagerView>
</View>
</SafeAreaView>
</Modal>
);
This is the result obtained when scrolling to the first page.
onPageScroll is called twice, and for some reason the wrong position is returned the second time.
And 0 is set to setCurrentTabIndex.
onPageScroll position = 1
onPageScroll position = 0
Is there any way to resolve this?
It may be related to what you write in <Modal></Modal> of 'react-native-modal'.
As also written here, this problem does not occur on iPhone11, but on iPhone12 and newer devices.

I didn't use 'react-native-modal', I used fullscreen modal.
screenOptions={{ presentation: 'modal' }}

Related

Populte WYSIWYG editor after react native fetch

I am trying to incorporate this WYSIWYG package into my react native project (0.64.3). I built my project with a managed workflow via Expo (~44.0.0).
The problem I am noticing is that the editor will sometimes render with the text from my database and sometimes render without it.
Here is a snippet of the function that retrieves the information from firebase.
const [note, setNote] = useState("");
const getNote = () => {
const myDoc = doc(db,"/users/" + user.uid + "/Destinations/Trip-" + trip.tripID + '/itinerary/' + date);
getDoc(myDoc)
.then(data => {
setNote(data.data()[date]);
}).catch();
}
The above code and the editor component are nested within a large function
export default function ItineraryScreen({route}) {
// functions
return (
<RichEditor
onChange={newText => {
setNote(newText)
}}
scrollEnabled={false}
ref={text}
initialFocus={false}
placeholder={'What are you planning to do this day?'}
initialContentHTML={note}
/>
)
}
Here is what it should look like with the text rendered (screenshot of simulator):
But this is what I get most of the time (screenshot from physical device):
My assumption is that there is a very slight delay between when the data for the text editor is actually available vs. when the editor is being rendered. I believe my simulator renders correctly because it is able to process the getNote() function faster.
what I have tried is using a setTimeOut function to the display of the parent View but it does not address the issue.
What do you recommend?
I believe I have solved the issue. I needed to parse the response better before assigning a value to note and only show the editor and toolbar once a value was established.
Before firebase gets queried, I assigned a null value to note
const [note, setNote] = useState(null);
Below, I will always assign value to note regardless of the outcome.
if(data.data() !== undefined){
setNote(data.data()[date]);
} else {
setNote("");
}
The last step was to only show the editor once note no longer had a null value.
{
note !== null &&
<RichToolbar
style={{backgroundColor:"white", width:"114%", flex:1, position:"absolute", left:0, zIndex:4, bottom: (toolbarVisible) ? keyboardHeight * 1.11 : 0 , marginBottom:-40, display: toolbarVisible ? "flex" : "none"}}
editor={text}
actions={[ actions.undo, actions.setBold, actions.setItalic, actions.setUnderline,actions.insertLink, actions.insertBulletsList, actions.insertOrderedList, actions.keyboard ]}
iconMap={{ [actions.heading1]: ({tintColor}) => (<Text style={[{color: tintColor}]}>H1</Text>), }}
/>
<RichEditor
disabled={disableEditor}
initialFocus={false}
onChange={ descriptionText => { setNote(descriptionText) }}
scrollEnabled={true}
ref={text}
placeholder={'What are you planning to do?'}
initialContentHTML={note}
/>
}
It is working properly.

React Native how to use fast image for expo using cache

I uploaded images to firebase storage and fetching it on the display. It's working fine, but I noticed that it reloads every time changing to other page and the speed is quite slow.
So, after googling I found expo-fast-image (because I'm using expo)
https://www.npmjs.com/package/expo-fast-image
so, after installing it, I'm trying to follow or copy the given an example, but I don't know how to use it properly. Below is my code with expo-fast-image.
Does anyone know how to use it properly?
import ExpoFastImage from 'expo-fast-image';
const CustomListItem = ({id, number, data, coffeeBean, description, image, Order}) => {
const user = auth.currentUser;
const name = user.displayName;
const ImageLoad = (image, id) => (
<View>
<ExpoFastImage
uri= {image}
CacheKey={`cache.${id}`}
/>
</View>
)
return (
<ListItem key={id} bottomDivider onPress={() => {Order({id, number, coffeeBean, description, image})}} >
<ExpoFastImage image={image, id}/>
<Avatar rounded source={{CacheKey: `cache.${id}`}} />
<ListItem.Content >
<ListItem.Title style={{ fontWeight: '800'}}>{id}</ListItem.Title>
<ListItem.Subtitle numberOfLines={1} ellipsizeMode='tail'>
Stock: {number}
</ListItem.Subtitle>
</ListItem.Content>
</ListItem>
)
}
export default CustomListItem
If expo-fast-image uses Image from react-native, images are cached and they are downloaded again only when the url changes. So in your situation, you might be giving different urls to the component which propmts it to download again. Make sure the url is always the same. Even if you add some random string like #some-random-value at the end of url which does nothing. It triggers the download action.

Marker not moving on map even after rerendering

I am fetching coordinates from my database every 5 seconds, and my marker is dynamic, this is my render method
console.log('render'+this.state.driverLocation.latitude +' '+this.state.driverLocation.longitude);
let marker = null;
marker = <MapView.Marker title='This is you' coordinate={this.state.driverLocation } />;
return (
<View style={styles.container}>
<MapView
initialRegion={this.state.focusedLocation}
region={!this.state.locationChosen ? this.state.focusedLocation : null}
style={styles.map}
onPress={this.pickLocationHandler}
ref={ref => this.map = ref}
>
{marker}
</MapView>
The log statement in render shows driverLocation changes every 5 sec as it should but the marker stays at the initial position (coordinates given while defining state).
Is there any problem with my code? Do I need to add something? Any help would be appreciated.
The problem was that android relies only on the key to change in order to update the custom marker and it can be solved by assigning a random key to the marker every time there's a need for rerendering.
So, something like this:
marker = <MapView.Marker
title='This is you'
coordinate={this.state.driverLocation}
key={ this.GenerateRandomNumber() }
} />;
While:
GenerateRandomNumber=()=>
{
var RandomNumber = Math.floor(Math.random() * 100) + 1 ;
return RandomNumber;
}

React-native - How to create a scrollable flatList in which the chosen item is the one at the middle?

I would like to create a scrollable FlatList to select only one item among a list. After the user scroll the list, the selected item will be the one in the colored rectangle (which have a fixed position) as you can see here :
Actually I'm only able to render a basic FlatList even after some researches.
Do you know how I should do that ?
I found the solution (but it's not a FlatList) !
To do that I use :
https://github.com/veizz/react-native-picker-scrollview.
To define the background of the current selected items I added a new props highLightBackgroundColor in the ScrollPicker Class in the index file of react-native-picker-scrollview :
render(){
...
let highLightBackgroundColor = this.props.highLightBackgroundColor || '#FFFFFF';
...
let highlightStyle = {
...
backgroundColor: highLightBackgroundColor,
};
...
How to use it :
<ScrollPicker
ref={sp => {
this.sp = sp;
}}
dataSource={['a', 'b', 'c', 'd', 'e']}
selectedIndex={0}
itemHeight={50}
wrapperHeight={250}
highLightBackgroundColor={'lightgreen'}
renderItem={(data, index, isSelected) => {
return (
<View>
<Text>{data}</Text>
</View>
);
}}
onValueChange={(data, selectedIndex) => {
//
}}
/>
How it looks without others customizations:
You can implement the same setup with the very popular react-native-snap-carousel package, using the vertical prop. No need to use a smaller, poorly documented/unmaintained package for this.

React Native FlatList with inverted option enabled

I have a FlatList as shown below:
<FlatList
inverted
data={messages}
keyExtractor={this._keyExtractor}
renderItem={({ item }) => (
<Text style={styles.item}>{item}</Text>
)}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={30}
/>
But here the OnEndReached method does not gets called when I reach the top of the flatlist.
Please help
OnEndReachedThreshold must be a number between 0 and 1. Since you are inverting your flatlist, onEndReachedThreshold would be the distance the user is from the top of the list [in percents]. Therefore a value of 0.5 would trigger the OnEndReached function when the user has scrolled through 50% of the viewable list.
To trigger the function at 50% your code should read something like this:
<FlatList
inverted
data={messages}
keyExtractor={this._keyExtractor}
renderItem={({ item }) => (
<Text style={styles.item}>{item}</Text>
)}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={0.5}
/>
All I could figure out was using of onScroll (performance beware) in here: https://snack.expo.io/#zvona/inverted-list-onbeginreached
The actual function looks like this:
checkIfBeginningReached = ({ nativeEvent }) => {
const { layoutMeasurement, contentOffset } = nativeEvent;
const currentPos = layoutMeasurement.height + contentOffset.y;
const listLength = ITEM_HEIGHT * this.state.items.length;
const reactThreshold = listLength - (ITEM_HEIGHT * THRESHOLD);
if (currentPos >= reactThreshold) {
this.fetchMoreItems(this.state.items.length);
}
}
On that, we pick up necessary info from nativeEvent (which kind of holds everything relevant). Then we just calculate the current position in pixels, length of whole list content in pixels and then threshold point.
In all, this particular solution requires two things:
1) list has fixed and same size of elements
2) list is not multi-column.
All the other functionality in the demo is just faking / mimicking one use case (of fetching 50 more items from server with 500ms delay). But I'll improve my answer if possible. But this should get you started.
My solution is here:
isCloseToBottom = ({layoutMeasurement, contentOffset, contentSize}) => {
const paddingToBottom = 1
return layoutMeasurement.height + contentOffset.y >=
contentSize.height - paddingToBottom}
just set onEndReachedThreshold={0.1} or onEndReachedThreshold={0.2}