How catch event onScoll or onScollEnd etc of HScrollview - reactjs

I am using
import {CollapsibleHeaderTabView} from "react-native-tab-view-collapsible-header";
import {HScrollView} from "react-native-head-tab-view";
I push a FlastList into HScrollView
<HScrollView
index={0}
style={{backgroundColor: "#FFFFFF"}}
// onScroll={(event)=>console.log("hitme2")}
// onScroll={()=>console.log("hitme")}
>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={item => item.id}
onEndReached={onEndReached}
/>
</>
Event onEndReached of flashlist will not working instead of that scroll off Hscrollview working.
I want catch event of Hscollview to make custom same onEndReached.
Please help me!
React: "16.13.1"
React Native: "https://github.com/expo/react-native/archive/sdk-41.0.0.tar.gz"
React-Native-Gesture-Handler: "^1.10.3"

You're in luck because I recently had a similar problem and found a solution :)
// imports
import React, {useCallback} from "react";
import {runOnJS, useAnimatedReaction, useSharedValue} from "react-native-reanimated";
import {CollapsibleHeaderTabView} from "react-native-tab-view-collapsible-header";
// component logic
const doSomething = useCallback((...args) => {/* do something */}, []);
const [scrollTrans, setScrollTrans] = useState(useSharedValue(0));
useAnimatedReaction(() => scrollTrans.value, (scrollTransValue) => {
console.log('scroll value', scrollTransValue);
/* do something here */
runOnJS(doSomething)(/* put args here */);
}, [doSomething, scrollTrans]);
const makeScrollTrans = useCallback((scrollTrans) => {
setScrollTrans(scrollTrans);
}, []);
// CollapsibleHeaderTabView
<CollapsibleHeaderTabView {...args} makeScrollTrans={makeScrollTrans}/>
You need to call some functions inside runOnJS function because react-native-reanimated since v2 running on a different thread
Also, instead of HScrollView you can use HFlatList like this:
<HFlatList
index={0}
data={data}
renderItem={renderItem}
keyExtractor={keyExtractor}
onEndReached={onEndReached}
{/* rest FlatList props */}
/>

Related

React Native TextInput onFocus does not allow onPress of other child components

Nested TextInput component does not allow other components' onPress function to be called. Only when the TextInput is not focused, the onPress works fine.
React Native Version : 0.66.3
Here is my code
export const Component = (props): JSX.Element {
const { searchText, onChangeSearchText, onClearSearchText } = props
const [searchViewFocused, setSearchViewFocused] = useState<boolean>(false)
const searchIcon = searchViewFocused ? "searchActive" : "searchInactive"
const cancelIcon = searchViewFocused ? "cancelActive" : "cancelInactive"
return (
<View style={styles.searchBoxContainer}>
<View style={[styles.searchBox, searchViewFocused && styles.searchBarActive]}>
<Icon styles={styles.searchIcon} icon={searchIcon} />
<TextInput
style={styles.searchInput}
onChangeText={onChangeSearchText}
value={searchText}
onFocus={() => setSearchViewFocused(true)}
onBlur={() => setSearchViewFocused(false)}
autoCompleteType={"off"}
numberOfLines={1}
/>
{searchText !== "" && (
<Pressable style={styles.clearIcon} onPress={onClearSearchText}>
<Icon icon={cancelIcon} />
</Pressable>
)}
</View>
</View>
)
})
Attached are the images of
Expected.
VS
The issue
When the cross icon is pressed on focused state, the textInput is unfocused rather What I am trying to achieve is that the search text gets cleared & the input remains focused.
Note: The onPress works perfectly fine when input is not focused
Any help is appreciated. Thanks!
PS: Tried TouchableOpacity & also I have tried wrapping the components inside a ScrollView to use keyboardShouldPersistTaps='handled' as mentioned in one of the SO answer but to no success.
Found a workaround to this is to wrap the whole component into a ScrollView and adding the prop keyboardShouldPersistTaps='handled'
Previously I was making the View inside the Component as ScrollView and adding keyboardShouldPersistTaps='handled' which did not work
export const Component = (props): JSX.Element {
...
return (
<ScrollView contentContainerStyle={styles.searchBoxContainer}
keyboardShouldPersistTaps='handled'>
...
</ScrollView>
)
})
The key was to wrap the entire component inside the ScrollView,
Here's what worked:
<ScrollView keyboardShouldPersistTaps='handled'>
<Component {...props}/>
</ScrollView>
Guess this was a silly mistake, but worth pointing out!
You're setting the focus of the entire component based on whether the TextInput has focus. Since the clear button is outside the text input, pressing it causes the component to lose focus.
One solution is to store the TextInput instance in a ref. When the clear button is pressed, you can refocus the text input. I've copied your component below and added some new lines, which are marked in comments.
export const Component = (props): JSX.Element {
const { searchText, onChangeSearchText, onClearSearchText } = props
const textInputRef = useRef(null); // new
const [searchViewFocused, setSearchViewFocused] = useState<boolean>(false)
const searchIcon = searchViewFocused ? "searchActive" : "searchInactive"
const cancelIcon = searchViewFocused ? "cancelActive" : "cancelInactive"
const onClear = () => {
onClearSearchText();
textInputRef.current?.focus();
} // new
return (
<View style={styles.searchBoxContainer}>
<View style={[styles.searchBox, searchViewFocused && styles.searchBarActive]}>
<Icon styles={styles.searchIcon} icon={searchIcon} />
<TextInput
ref={textInputRef} // new
style={styles.searchInput}
onChangeText={onChangeSearchText}
value={searchText}
onFocus={() => setSearchViewFocused(true)}
onBlur={() => setSearchViewFocused(false)}
autoCompleteType={"off"}
numberOfLines={1}
/>
{searchText !== "" && (
<Pressable style={styles.clearIcon} onPress={onClear}> // calls new function
<Icon icon={cancelIcon} />
</Pressable>
)}
</View>
</View>
)
})

Why my modal does not show up in react native iOS. But its working in react native android

I am in the strange situation. I always develop my app using android simulator. Since running two simulators both iOS and android always turn my pc into oven.
I am using redux in this app.
I got a component called cart. Whenever user tried to access this component I am testing if the user got item added to the cart or not. I am using useEffect hook to test it.
My cart component look like this
//Fetch cart data
let cartList = useSelector((state) => state.productReducer.cartList);
useEffect(() => {
if (cartList?.OrderDTO?.Id === null) {
setModalVisible(true);
//alert("your Cart is empty")
}
}, []);
useEffect(() => {
if (cartList?.OrderDTO?.Id === null) {
setModalVisible(true);
//alert("your Cart is empty")
}
}, [cartList]);
return (
<ScrollView style={styles.pageContainer}>
<Loader loading={loading} />
<View>
<NotiDialog
modalVisible={modalVisible}
setVisible={setModalVisible}
contentText="Your cart is empty"
comfirmText="OK"
comfirm={() => handleEmptyCart()}
titleText="Notification"
/>
<ComfirmDialog
modalVisible={modalTwoVisible}
setVisible={setModalTwoVisible}
contentText="Are you sure you want to empty the cart? "
comfirmText="OK"
comfirm={() => emptyTheCart()}
titleText="Notification"
/>
</View>
I already fetch cartData once when the app is loaded
As you can see in the useEffect I am using setModalVisible(true) to show up the modal.
But the problem is that this modal popup in android always. I already tried about 100 times. But in iOS its never show up
So instead of modal I tried to open up the ***commented*** alert in iOS. The message your Cart is empty popup. But the modal doesn't. So is there something wrong with my modal?
Here is my modal component
import {
...,
Modal,
} from 'react-native';
const NotiDialog = ({
modalVisible,
setVisible,
comfirm,
comfirmText,
contentText,
titleText,
}) => {
const confirmFunc = () => {
comfirm();
setVisible(false);
};
return (
<View style={styles.centeredView}>
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => {
setModalVisible(!modalVisible);
}}>
<View style={styles.centeredView}>
...
</View>
</Modal>
</View>
);
};
try setTimeout() before opening the modal

FlatList, Props ListHeaderComponent and Dynamic flatlist data

Sorry for the title, I wasn't inspired, and I didn't know what title to put for my problem.
I'm using Socket.io on my React native application to render some dynamics datas.
I'm using FlatList to display all this datas, and I need to show some contents like filters and buttons at the top of the FlatList.
So In my FlatList Props, I used ListHeaderComponent and in this component I display one other flatlist and contents.
My problem is :
When the main FlatList is updated with one of the Socket.io response (Every 5sec), all the components displayed by ListHeaderComponent aren't usable for a bit of time.
This period is like 500ms or less, but in the ListHeaderComponent I have some buttons / filters, and they are not clickable during this period.
And the problem is repeated each time the FlatList data is updated.
You can see my code (simplyfied) here :
export default function MyList({ navigation }) {
const [myData, setMyData] = useState<ResponseType[]>([])
// Socket Io
const socket = io(SOCKET_URL, { forceNew: true, timeout: 5000 })
useEffect(() => {
socket.on("connect", () => {
console.log("Connected to Socket ID : ", socket.id)
})
return () => {
socket.disconnect()
socket.close()
}
},[])
socket.on("socketResponse", (data: ResponseType) => {
setMyData(prevDate => [...prevDate, data])
})
const renderFilterItem = ({ item }: { item: Filter }) => <Text key={ item.id }>{ item.title }</Text>
const renderListItem = ({ item }: { item: ResponseType }) => <Text key={ item.id }>{ item.id }</Text>
const ListTop = () => (
<>
<TouchableOpacity onPress={() => navigation.navigate('HomePage')}>
<Text>BACK CONTENT</Text>
</TouchableOpacity>
<FlatList
horizontal
showsHorizontalScrollIndicator={false}
data={someFilters}
keyExtractor={(item) => item.id}
renderItem={renderFilterItem}
/>
</>
)
return (
<FlatList
ListHeaderComponent={ListTop}
data={myData}
keyExtractor={(item) => item.id}
renderItem={renderListItem}
maxToRenderPerBatch={5}
windowSize={2}
/>
)
}
I don't know how to explain better my problem. But If I move the component of ListHeaderComponent above the FlatList, there is no more problem.
But If I do this, just the FlatList is scrollable, and I want all the page scrollable.
I can't wrap my Flatlist with a ScrollView because of this error : VirtualizedLists should never be nested inside plain ScrollViews with the same orientation
Has anyone faced this problem ?
Thanks
try to use optimization for ListTop component as a possible problem here would be rendering of your functional component due to change of parent one.
here is the code sample.
import React, {useCallback} from 'react';
// your other codes
const ListTop = useCallback(() => (
<>
<TouchableOpacity onPress={() => navigation.navigate('HomePage')}>
<Text>BACK CONTENT</Text>
</TouchableOpacity>
<FlatList
horizontal
showsHorizontalScrollIndicator={false}
data={someFilters}
keyExtractor={(item) => item.id}
renderItem={renderFilterItem}
/>
</>
), [someFilters]);
here the component will be memoized until there is no change in someFilters.

How to architect a single modal component accessible by multiple components in React Native?

I'm new to react and I'm having trouble getting my head around the architecture of my app. Here's what I've got:
I use fetch() to get an array of records (records) from the backend. I use a couple of custom components to handle outputting the data. At the end of the tree is a series of touchable components which I need to open a single modal component on press. Here's a simple version of the code:
./app.js
<ScrollView>
{records.map((record, index) => {
return <Post key={index} post={record} />
})}
</ScrollView>
./src/components.js
function Post(props) {
return (
<Child info={props.post} />
...other custom components in here...
)
}
function Child(props) {
return (
<TouchableHighlight onPress={() => ...open modal...}>{props.info}</TouchableHighlight>
)
}
So I can't put my <Modal> in either of the components in components.js, as then I end up with one modal for each record in the database. So I figure I need to put it at the end of the <ScrollView> in app.js, but then how do I open and close it from the <Child> component? I've read that it's possible to pass info back up the chain to a parent component using a callback function, but I'm unsure of how to do that while also passing props down that contain the information of each record.
You can try this logic:
const [modalVisible, setModalVisible] = useState(false);
const handleVisibility = () => {
setModalVisible(!modalVisible)
}
return(
<View>
<YourWrappingModalComponent visible={modalVisible} />
<ScrollView>
{records.map((record, index) => {
return <Post
key={index}
post={record}
handleVisibility={handleVisibility} />
})}
</ScrollView>
</View>
)
To open modal from Child you do:
function Post(props) {
return (
<Child info={props.post} handleParentModal={props. handleVisibility} />
...other custom components in here...
)
}
function Child(props) {
return (
<TouchableHighlight onPress={() => props.handleParentModal()}>{props.info}</TouchableHighlight>
)
}
Using a component like YourWrappingModalComponent where you define your modal is very useful if you use your modal in many places.

how to click a button programmatically in react native

i have tried couple of ways to figure out how to perform click event programmatically, i found that useRef can solve the problem. but i tried it doesn't seems to work for me. below is the code,
//declaration
const reff=useReff(null);
//button design
<Button
onPress={()=>{console.log('Pressed')}}
title="Press me"
ref={reff}
/>
//Triggering the event
reff.current.onPress();
its giving me an error "onPress is undefined". i have tried with onClick(),click(),press() nothing worked for me unfortunately.
can someone help me on this please..
import React, { useRef } from 'react';
import { Button, View } from 'react-native';
const App = () => {
const btnRef = useRef();
return (
<View>
<Button
title="Ref"
onPress={() => {
console.info('Ref props', btnRef.current.props);
btnRef.current.props.onPress();
btnRef.current.props.onHandlePress();
}}
/>
<Button
title="Click Me"
ref={btnRef}
onPress={() => console.log('onPress props called')}
onHandlePress={() => console.log('Custom onHandlePress called')}
/>
</View>
);
};
export default App;
I'm using react native cli
"react": "16.13.1",
"react-native": "0.63.4"
I guess, whatever props that passed in Button component can be accessed by calling ref.current.props.anyprops, Good luck!
Try this once, you just need to set ref :
<Button id={buttonId}
onClick={e => console.log(e.target)}
ref={(ref) => { this.myButton = ref; }}
/>
and then you can add this in your function :
yourFunction = () => {
this.myButton.click()
}
Hope it helps. feel free for doubts
This works for me:
<Button
onClick={() => console.log('pressed')}
title="Press me"
/>
Just use the curly braces when you write the callback functions on multiple lines.

Resources