React-Native: RefreshControl on ScrollView with nested View won't show animation - reactjs

So I am pretty new to React Native and I'm trying to implement a list of contacts which is refreshable. However I cannot get it to work (probably because I missed something).
I don't get any errors, I simply cannot pull down to get the animation.
I have created a wrapper component which is used as a basis for all my other stuff besides the contact-list as well.
const Wrapper = ({ refreshing, onRefresh, children }) => {
const theme = useColorScheme();
return refreshing === null || onRefresh == null ? (
<SafeAreaView style={{ flex: 1 }}>
<ScrollView
style={theme === "light" ? styles.container_light : styles.container_dark}
contentContainerStyle={{ flexGrow: 1 }}
>
<View style={styles.wrapper}>{children}</View>
</ScrollView>
</SafeAreaView>
) : (
<SafeAreaView style={{ flex: 1 }}>
<ScrollView
style={theme === "light" ? styles.container_light : styles.container_dark}
contentContainerStyle={{ flexGrow: 1 }}
>
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
<View style={styles.wrapper}>{children}</View>
</ScrollView>
</SafeAreaView>
);
};
export default Wrapper;
As seen above, refreshing & onRefresh get passed to the Wrapper, because the ScrollView is located there.
Now in my contact list, I put all my contacts inside that wrapper element and pass refreshing & onRefresh to the Wrapper. I also have a header and a FloatingActionButton on that screen. The screen takes up 100% of the height because I want that FloatingActionButton on the bottom right of my screen.
// outside my contact-list component:
const wait = (timeout) => {
return new Promise((resolve) => setTimeout(resolve, timeout));
};
// in my contact-list component:
const [refreshing, setRefreshing] = useState(false);
const onRefresh = useCallback(() => {
setRefreshing(true);
wait(2000).then(() => setRefreshing(false));
}, []);
<Wrapper refreshing={refreshing} onRefresh={onRefresh}>
<Text style={theme === "light" ? globalStyles.heading_light : globalStyles.heading_dark}>
{t("ContactsIndexHeader")}
</Text>
<View style={{ marginTop: 30 }}>
{contacts.length > 0 ? letters.map((letter) => {
return (...); // Check last name of contact & display if equal to letter
})
: null}
</View>
<FloatingActionButton icon="plus" onPress={() => navigation.navigate("ContactsCreate")} />
</Wrapper>

I believe that refresh control should be specified as a prop and not as a child of scroll view, example:
<ScrollView
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
/>
}
/>

Related

#gorrhom React Native Bottom Sheet - Calling from another component

Im trying to implement this relatively popular bottom sheet in React Native by #gorhom.
My aim is to open the bottom sheet from another component. I've tried to follow this answer - here . However, i do not understand what goes in the touchable opacity onPress in my component where it is being called from.
Code below
BottomSheetModalComponent
export default function BottomSheetModalComponent({ref}) {
// ref
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
// variables
const snapPoints = useMemo(() => ['25%', '50%'], []);
// callbacks
const handlePresentModalPress = useCallback(() => {
ref.current?.present();
}, []);
const handleSheetChanges = useCallback((index: number) => {
console.log('handleSheetChanges', index);
}, []);
// renders
return (
<BottomSheetModalProvider>
<View>
<BottomSheetModal
ref={ref}
snapPoints={snapPoints}
onChange={handleSheetChanges}
>
<View>
<Text>Awesome 🎉</Text>
</View>
</BottomSheetModal>
</View>
</BottomSheetModalProvider>
);
};
Location Component
export default function LocationBar() {
// Create Ref
const userBottomSheetRef = useRef<BottomSheetModal>(null);
// Pass ref into the bottom sheet component
<LocationBottomSheet ref={userBottomSheetRef} snapPoints={["30%"]}/>
return (
<>
<View style={{ flexDirection: "row" }}>
<View style={styles.locationBar}>
<Octicons style={styles.locationIcon} name="location" size={18} />
<TouchableOpacity onPress={() => {
//WHAT GOES HERE?
}}>
<Text style={{fontSize: 17, fontWeight: '600'}}>{userLocation}</Text>
</TouchableOpacity>
<Ionicons style={styles.chevronIcon} name="chevron-down" size={12} />
</View>
</View>
</>
);
}
Thanks in advance

React Native Flatlist is not scrolling. Not able to use refresh list as scroll is not working

React Native Flat list will now scroll. Not sure what I am doing wrong here. I am on the latest version for React Native. I need the flatlist to scroll and with this issue I can't refresh the list when I pull down the list to refresh. I also tried to user the prop to enable scroll and that did not work either.
<View
style={[
styles.container,
{paddingLeft: insets.left, paddingRight: insets.right},
]}>
<List.Section style={styles.flex}>
<List.Subheader>Profile List:</List.Subheader>
<FlatList
data={data}
style={styles.flex}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={() => setRefreshing(true)}
tintColor={'blue'}
/>
}
renderItem={({item, index, separators}) => (
<>
<List.Item
title={item.title.rendered}
description={item.content.rendered.replace(/(<([^>]+)>)/gi, '')}
left={props => <List.Icon {...props} icon="layers" />}
right={props => (
<Button
style={{alignSelf: 'center'}}
mode="text"
onPress={() => selectedProfile(item.acf.elmc_custom_layout)}
onLongPress={() => showDialog(item)}>
Download
</Button>
)}
/>
<Divider />
</>
)}
/>
</View>
// my stylesheet for this component
const styles = StyleSheet.create({
container: {
flex: 1,
},
flex: {
flex: 1,
},
});
I found issue but not sure how to fix it. I am using the following code so that it will dismiss the keyboard when I press anywhere on the screen. This code dismiss the keyboard but it prevents the screens from scrolling. I placed it to wrap around my navigation so that all screen will dismiss the keyboard but it causing issues with scrolling the screen. can someone provide an example how I can dismiss keyboard when I press anywhere on the screen and also be able to scroll the screens when needed
// allows the keyboard to be dismissed anywhere the screen is pressed
const DismissKeyboard = ({children}) => (
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={{flex: 1}} onStartShouldSetResponder={() => true}>
{children}
</View>
</TouchableWithoutFeedback>
);
function CustomLayoutNavigator(props) {
return (
<DismissKeyboard?
<Stack.Navigator>
<Stack.Screen
name="CustomLayoutHomeScreen"
component={CustomLayoutScreen}
initialParams={{myData: []}}
options={{
title: 'My Layout',
headerShown: false,
}}
/>
Try to use ScrollView instead of View on the top level.
Try this,
const [refreshing, setRefreshing] = useState(false);
const onRefresh = () => {
//refresh Action
};
<FlatList
data={data}
style={styles.flex}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={() => onRefresh()}
tintColor="#F8852D"
/>
}
renderItem={({item, index, separators}) => (
)}
/>

Invariant Violation: Text strings must be rendered within a <Text> component while using flatList

I am using flat list to display data which is coming from unsplash api. But here it keeps on complaining to saying this
Invariant Violation: Text strings must be rendered within a component
I am not even using any text component. I have no idea what is wrong here.
App.js
export default function App() {
const [loading, setLoading] = useState(true);
const [image, setImage] = useState([]);
const {height, width} = Dimensions.get('window');
const URL = `https://api.unsplash.com/photos/random?count=30&client_id=${ACCESS_KEY}`;
useEffect(() => {
loadWallpapers();
}, [])
const loadWallpapers =() => {
axios.get(URL)
.then((res) => {
setImage(res.data);
setLoading(false);
}).catch((err) => {
console.log(err)
}).finally(() => {
console.log('request completed')
})
}
const renderItem = (image) => {
console.log('renderItem', image);
return (
<View style={{height, width}}>
<Image
style={{flex: 1, height: null, width: null}}
source={{uri : image.urls.regular}}/>
</View>
)
}
return loading ? (
<View style={{flex: 1, backgroundColor: 'black', justifyContent: 'center',alignItems: 'center'}}>
<ActivityIndicator size={'large'} color="grey"/>
</View>
): (
<SafeAreaView style={{flex: 1, backgroundColor: 'black'}}>
<FlatList
horizontal
pagingEnabled
data={image}
renderItem={({ item }) => renderItem(item)} />}
/>
</SafeAreaView>
)
}
I thing data of Flatlist is null, try
<FlatList
horizontal
pagingEnabled
data = {image ? image : []}
renderItem={({ item }) => renderItem(item)} />}
/>
I needed to do something like this to make it work.
const renderItem = ({ item }) => { <---- I have destructured item here
console.log(item)
return (
<View style={{ flex: 1 }}>
</View>
);
};
<FlatList
scrollEnabled={!focused}
horizontal
pagingEnabled
data={image}
renderItem={renderItem}
/>

Is it possible to prevent a modal from hiding if it's visibility prop rerenders?

I have a component with four different lists of items. An item is placed in a list depending on the date. When the user clicks a button to change the date of that item a modal appears with a date picker (the item's dateModalVisbility prop switches to true). This works as expected but then if an item's date change causes it to switch lists, the date picker modal disappears without the prop switching to false. Then when you click the button to reopen it switches to false and then after another press switches back to true and reopens. I believe this is caused by the rerender of the item when it switches into a different list.
I would like to force the modal to stay open even when it switches lists. I have tried making the different lists functions that I call before anything else renders but this did not solve the issue. I am out of ideas and unsure if this is even possible to do.
My flatlists (this.props.todos comes from redux):
<FlatList
data={_.sortBy(this.props.todos, item => {
return item.date;
})}
extraData={this.props.todos}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => {
if (moment().isSame(item.date, 'day')) {
return (
<TodoItem
todoItem={item}
deleteTodo={() => this.props.removeTodo(item)}
/>
);
}
}}
/>
<View style={styles.headerViewStyle}>
<Text style={styles.headerTextStyle}>Tomorrow</Text>
</View>
<FlatList
data={_.sortBy(this.props.todos, item => {
return item.date;
})}
extraData={this.props.todos}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => {
if (
moment()
.add(1, 'day')
.isSame(item.date, 'day')
) {
return (
<TodoItem
todoItem={item}
deleteTodo={() => this.props.removeTodo(item)}
/>
);
}
}}
/>
<View style={styles.headerViewStyle}>
<Text style={styles.headerTextStyle}>Upcoming</Text>
</View>
<FlatList
data={_.sortBy(this.props.todos, item => {
return item.date;
})}
extraData={this.props.todos}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => {
if (
moment()
.add(1, 'day')
.isBefore(item.date, 'day')
) {
return (
<TodoItem
todoItem={item}
deleteTodo={() => this.props.removeTodo(item)}
/>
);
}
}}
/>
<View style={styles.headerViewStyle}>
<Text style={styles.headerTextStyle}>Sometime</Text>
</View>
<FlatList
data={_.sortBy(this.props.todos, item => {
return item.date;
})}
extraData={this.props.todos}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => {
if (moment().isAfter(item.date, 'day') || item.date === null) {
return (
<TodoItem
todoItem={item}
deleteTodo={() => this.props.removeTodo(item)}
/>
);
}
}}
/>
The todoItem component:
class TodoItem extends Component {
render() {
const todoItem = this.props.todoItem;
return (
<View>
<ItemSwipeRow item={todoItem} completeItem={this.props.deleteTodo}>
<TouchableHighlight
onPress={() => this.props.toggleItemMenu(todoItem)}
underlayColor={null}>
<ListItem
containerStyle={styles.todoItem}
contentContainerStyle={styles.contentStyle}
title={todoItem.text}
titleStyle={{ color: '#FCEFEF', fontSize: 16 }}
rightElement={todoItem.date ? this.renderDate.bind(this)() : null}
/>
</TouchableHighlight>
</ItemSwipeRow>
{todoItem.itemMenuToggled ? <ItemMenuBar item={todoItem} /> : null}
{this.props.reminderToggleActive && todoItem.date ? (
<ReminderToggleButtons item={todoItem} />
) : null}
<NotesModal item={todoItem} />
{todoItem.dateModalVisible ? <DatePickerModal item={todoItem} /> : null}
</View> //this line above is responsible for displaying the date picker modal
);
}
}
And the DatePickerModal:
class DatePickerModal extends Component {
render() {
return (
<Modal transparent animationType="fade" visible>
<View style={styles.containerStyle}>
<View style={styles.modalContainer}>
<View style={{ justifyContent: 'flex-end', flexDirection: 'row' }}>
<View style={{ padding: 5 }}>
<Feather
name="x-square"
size={35}
color={'#db5461'}
onPress={() => this.props.toggleDateModal(this.props.item)}
/>
</View>
</View>
<View style={{ padding: 5 }}>
{Platform.OS === 'ios' ? (
<IosDatePicker item={this.props.item} />
) : (
<AndroidDatePicker />
)}
</View>
</View>
</View>
</Modal>
);
}
}
I can provide the actions and button that opens the item but I don't think that's where the issue is coming from. The button/actions are doing their job right.
I would re-arrange the way you are using the modal. Instead of each component having a modal add one modal to the top level component and then use props to set the items that was selected. Somthing like this:
export default class TopComponent extends React.Component {
this.state = {
selectedItem: null,
modalvisible: false,
}
render = () => {
return (
<FlatList renderItem={({item, key} => (<MyItem onItemSelected={item => this.setState({ selectedItem: item, modalVisible: true}))} />} />
<Modal visible={this.state.modalVisible}>
<Text>this.state.selectedItem</Text>
</Modal>
}
}
Here is a quick codesandbox to show some more details: https://codesandbox.io/s/react-native-6518p

React Native Flatlist header re-rendering when scroll

I am new to React Native and I am having problems with the header in a FlatList.
The header re-renders as soon as I start to scroll, this creates a flickering effect on the images I have in the header.
I have been searching for an answer everywhere but I have not find a posible solution.
¿how could I configure it to stop re-rendering the header when scrolling the list?
....
const Item = ({ title }) => {
return (
<View style={styles.card}>
<Text style={styles.title}>{title}</Text>
</View>
);
};
const listHeader = () => {
const categoriesButtons = categories.map(cat => (
<CategoryButton
text={cat.name}
icon={cat.code}
key={cat.code}
onPress={() => {
//#Todo logic to filter promotions accordingly with the category pressed
}}
/>
));
return (
<View>
<View style={styles.filtersContainer}>
<ImageBackground
source={images.bgShape}
style={{ width: '100%', height: 140 }}
resizeMode="stretch"
>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
{categoriesButtons}
</ScrollView>
</ImageBackground>
</View>
<View style={styles.breadcrumbContainer}>
<Breadcrumbs navigation={navigation} stack={routes} />
</View>
<View style={styles.titleContainer}>
<Text style={sharedStyles.titleText} id="main-title">
¡{totalOfPromotions} promociones activas en Medellín!
</Text>
</View>
</View>
);
};
return (
<LinearGradient
colors={[Colors.BG_START, Colors.BG_END]}
style={styles.mainContainer}
>
{loading ? (
<ActivityIndicator size="large" color="#000000" />
) : (
<FlatList
data={promos}
renderItem={({ item }) => <Item title={item.title} />}
keyExtractor={(item, index) => index.toString()}
ListHeaderComponent={listHeader}
showsVerticalScrollIndicator={false}
onEndReached={showPromos}
onEndThreshold={0.7}
/>
)}
</LinearGradient>
);
};
listHeader() function is being called more than once because in Flatlist tag should be called as
<FlatList
data={promos}
renderItem={({ item }) => <Item title={item.title} />}
keyExtractor={(item, index) => index.toString()}
ListHeaderComponent={listHeader()}
showsVerticalScrollIndicator={false}
onEndReached={showPromos}
onEndThreshold={0.7}
/>
Use () while assigning ListHeaderComponent prop. By this way, function will be invoked only once.
Hope this help you. Enjoy coding!
From what I can see in the code you provided that you are defining the ListHeader component inside your other parent component which will redfine it on every render.
Moving it might outside the parent component might help.
You can fix your flickering issue by memoizing your ListHeaderComponent.
In your case just wrap your component with useMemo:
const listHeader = useMemo(() => {
...
})

Resources