React native calendar - react-native

I am trying to implement react native calendar agenda with marked dates and items dynamically from Firebase. I am not sure how to create the array required for this display. Below code gets data from firebase but I need to display the same. Please help me in creating array required by react native calendar
import React, {useState, useContext, useEffect} from 'react';
import { View, TouchableOpacity, StyleSheet } from 'react-native';
import { CalendarList, Agenda } from "react-native-calendars";
import { Button, Card, Layout, Text } from '#ui-kitten/components';
import moment from 'moment';
import AppContext from './AppContext';
import firebase from '../firebase';
function Calendar() {
const todaysDate = new moment(new Date()).format("DD-MM-YYYY")
const [items, setItems] = useState([]);
const myContext = useContext(AppContext);
const groups = myContext.userGroups;
useEffect(() => {
function loadItems() {
firebase.database().ref("events").on("value", (userSnapshot) => {
userSnapshot.forEach(grpid => {
grpid.forEach(element => {
const gid = grpid.key;
groups.forEach(usergroupid => {
const usergrpid = usergroupid.groupid;
if(usergrpid === gid){
const id = element.key;
const sdate = moment((element.child('start').val()), 'DD-MM-YYYY');
console.log("sdate", sdate);
const start = moment(sdate).toDate();
console.log("start", start);
const edate = moment((element.child('end').val()), 'DD-MM-YYYY');
const end = moment(edate).toDate();
const title = element.child('title').val();
//items[sdate] = [];
items[sdate] = {name:title}
setItems(items);
console.log("new",items);
}
});
});
});
});
}
loadItems();
}, [])
const renderItem = (items) => {
return(
<TouchableOpacity>
<Card>
<View>
<Text>{items}</Text>
</View>
</Card>
</TouchableOpacity>
)
}
return (
<View style={{ paddingTop: 50, flex: 1 }}>
<Agenda
current={todaysDate}
minDate={todaysDate}
markedDates={{
'2020-12-24': [{selected: true, marked: true, selectedColor: 'blue', Name:"hello"}],
'2020-12-25': {marked: true},
'2020-12-26': {marked: true, dotColor: 'red', activeOpacity: 0},
}}
items={items}
renderItem={renderItem}
/>
</View>
);
}
export default Calendar;

You only need to add square braces around the dates
markedDates={{
[start]: {selected: true, marked: true, selectedColor: 'blue', Name:"hello"},
[edate]: {marked: true},
[end]: {marked: true, dotColor: 'red', activeOpacity: 0},
}}

Related

Get current index/visible item of FlatList

I have a scrolling view of posts. Each post has a corresponding user and I have a header that shows the user info of the current visible post. With Flutter this was simple, I just wrapped the post widget with a visibility detector. With React Native this is not very easy. I've tried onViewableItemsChanged but, since I am using a fuction not a class, that causes an error. I also tried some solutions that used onScroll and onMomentumScrollEnd but those all just stayed at index 0. How can I get the current index that is fully visible? If needed, I am fine with splitting up the pagination functions so I can just have a class with the UI and use onViewableItemsChanged but I don't know how to do that because the handleLoadMore function is used in the UI.
export default function PostsListView() {
const [users, setUsers] = useState<User[]>([]);
const [posts, setPosts] = useState<Post[]>([]);
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(true);
const [hasMore, setHasMore] = useState(true);
const onScroll = useCallback((event: any) => {
const slideSize = event.nativeEvent.layoutMeasurement.width;
const index = event.nativeEvent.contentOffset.x / slideSize;
const roundIndex = Math.round(index);
console.log("roundIndex:", roundIndex);
currentItem = roundIndex;
}, []);
useEffect(() => {
async function fetchPosts() {
setLoading(true);
const { data, error } = await supabase
.from("posts")
.select("*")
.order("date", { ascending: false })
.range((page - 1) * PAGE_SIZE, page * PAGE_SIZE - 1);
if (error) {
console.error(error);
showAlert();
setLoading(false);
return;
}
const newPosts = data.map((post: any) => new Post(post));
setPosts((prevPosts) => [...prevPosts, ...newPosts]);
setLoading(false);
setHasMore(data.length === PAGE_SIZE);
}
async function fetchUsers() {
const { data, error } = await supabase.from("posts").select("*");
if (error) {
showAlert();
console.error(error);
return;
}
const newUsers = data.map((user: any) => new User(user));
newUsers.forEach((user) => {
const userPosts = posts.filter((post) => post.uid === user.uid);
user.posts = [...user.posts, ...userPosts];
});
setUsers((prevUsers) => [...prevUsers, ...newUsers]);
}
fetchPosts();
fetchUsers();
}, [page]);
const handleLoadMore = () => {
if (!loading && hasMore) {
setPage((prevPage) => prevPage + 1);
}
};
const handleScroll = (event: any) => {
const index = Math.floor(
Math.floor(event.nativeEvent.contentOffset.x) /
Math.floor(event.nativeEvent.layoutMeasurement.width)
);
currentItem = index;
};
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
{loading ? (
<Text>Loading...</Text>
) : (
<FlatList
data={posts}
horizontal={false}
directionalLockEnabled={true}
renderItem={({ item }) => (
<View>
<HomePost
post={item}
user={
users.filter(function (u) {
return u.uid == item.uid;
})[0]
}
index={posts.indexOf(item)}
loading={loading}
/>
<SizedBox vertical={5} />
</View>
)}
keyExtractor={(item) => item.postId}
onEndReached={handleLoadMore}
onEndReachedThreshold={0.1}
onScroll={onScroll}
onMomentumScrollEnd={onScroll}
/>
)}
</View>
);
}
If you provide a viewabilityConfig to the FlatList, you can use the onViewableItemsChanged event to learn which items are on screen. You just have to make sure that both the viewabilityConfig and onViewableItemsChanged values never change:
import { useState, useEffect, useRef, useCallback } from 'react';
import { Text, View, StyleSheet, FlatList, Image } from 'react-native';
import Constants from 'expo-constants';
// You can import from local files
import AssetExample from './components/AssetExample';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
const API_URL = 'https://random-data-api.com/api/v2/users?size=25';
export default function App() {
const [posts, setPosts] = useState([]);
const [visibleItems, setVisibleItems] = useState([]);
// wrapped in ref so that re-renders doesnt recreate it
const viewabilityConfig = useRef({
minimumViewTime: 100,
itemVisiblePercentThreshold: '90%',
}).current;
// wrapped in useCallback so that re-renders doesnt recreate it
const onViewableItemsChanged = useCallback(({ viewableItems }) => {
setVisibleItems(viewableItems.map(({ item }) => item));
}, []);
useEffect(() => {
fetch(API_URL)
.then((data) => data.json())
.then(setPosts);
}, []);
return (
<View style={styles.container}>
{visibleItems.length > 0 && (
<Text>
Currently visible:{' '}
{visibleItems
.map((item) => item.first_name + ' ' + item.last_name)
.join(', ')}
</Text>
)}
<View style={styles.flatlistContainer}>
<FlatList
data={posts}
renderItem={(props) => <Item {...props} />}
viewabilityConfig={viewabilityConfig}
onViewableItemsChanged={onViewableItemsChanged}
/>
</View>
</View>
);
}
const Item = ({ item }) => {
return (
<View style={styles.itemContainer}>
<Text>
{item.first_name} {item.last_name}
</Text>
<Image
source={{ uri: item.avatar }}
style={{ width: 100, height: 100 }}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
flatlistContainer: {
width: '100%',
height: 500,
backgroundColor: 'lightblue',
},
itemContainer: {
justifyContent: 'center',
alignItems: 'center',
margin: 10,
},
});
Demo
You could get the currently viewable items with onViewableItemsChanged where you should get your information.
<FlatList
data={posts}
horizontal={false}
directionalLockEnabled={true}
// this should return an array with following infos
// [{
// item: {key: "key-12"},
// key: "key-12",
// index: 11,
// isViewable: true
// }]
onViewableItemsChanged={({changed, viewableItems}) => console.log(changed, viewableItems)}
....
I've gone through and created a supabase app that resembles your use case and the onViewableItemsChanged approach should work:
import { useContext, useEffect, useState, useRef, useCallback } from 'react';
import {
View,
StyleSheet,
Dimensions,
FlatList,
ActivityIndicator,
} from 'react-native';
import { Text } from 'react-native-paper';
import { SessionContext } from '../../Context';
import { supabase } from '../../initSupabase';
import PostItem from '../../components/PostItem';
const { height } = Dimensions.get('screen');
const totalPostsToGet = 2;
type Post = {
post: {
text: string;
media: {
type: string;
source: string;
};
};
id: number;
created_at: string;
uid: string;
};
export default function PostScreen(props) {
const { session } = useContext(SessionContext);
const [posts, setPosts] = useState<Post[]>([]);
const [page, setPage] = useState(1);
const [isLoading, setIsLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const [visibleItems, setVisibleItems] = useState([]);
const viewabilityConfig = useRef({
// minimumViewTime: 100,
itemVisiblePercentThreshold: 50,
}).current;
// wrapped in useCallback so that re-renders doesnt recreate it
const onViewableItemsChanged = useCallback(({ viewableItems }) => {
setVisibleItems(viewableItems.map(({ item }) => item));
}, []);
const handleLoadMore = () => {
if (!isLoading && hasMore) {
setPage((prevPage) => prevPage + 1);
}
};
useEffect(() => {
if (!session) return;
const fetchLastPost = async () => {
const { data, error } = await supabase
.from('posts')
.select('*')
.order('created_at')
.range(0, 1);
return data[0];
};
const fetchPosts = async () => {
setIsLoading(true);
const lastPost = await fetchLastPost();
console.log('last post', lastPost);
const rangeStart = (page - 1) * totalPostsToGet;
const { data, error } = await supabase
.from('posts')
.select('*')
.order('created_at', { ascending: false })
.range(rangeStart, rangeStart + totalPostsToGet - 1);
if (error) {
console.log('error retrieving profile data', error);
}
if (data) {
setPosts((prev) => prev.concat(data));
// I couldnt figure out how PAGE_SIZE could be used to know that the last post was reached
// so I just grab the last post and look to see if its id is in current data
const hasLastPost = Boolean(
data.find((post) => post.id == lastPost.id)
);
setHasMore(!hasLastPost);
}
setIsLoading(false);
};
fetchPosts();
// subscribe to database changes
const subscription = supabase
.channel(`Posts`)
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'posts',
// filter: `id=eq.${session.user.id}`,
},
(payload) => {
setIsLoading(true);
console.log('Post update');
setPosts((prev) => {
// either push new post or update existing one
const postIndex = prev.findIndex(
(post) => post.id == payload.new.id
);
if (postIndex < 0) return [...prev, payload.new];
else {
const newPosts = [...prev];
newPosts[postIndex] = payload.new;
return newPosts;
}
});
setIsLoading(false);
}
)
.subscribe();
return () => {
supabase.removeChannel(subscription);
};
}, [session, page]);
return (
<View style={styles.container}>
<Text>Currently visible:</Text>
<View style={{ padding: 5, margin: 5 }}>
{visibleItems.map((item) => (
<Text>{item.post.text.substring(0, 30)}</Text>
))}
</View>
<View style={styles.flatlistContainer}>
{isLoading && <ActivityIndicator />}
<FlatList
data={posts}
keyExtractor={(item: Post) => item.id}
horizontal={false}
directionalLockEnabled
onEndReached={handleLoadMore}
renderItem={(props) => <PostItem {...props} />}
viewabilityConfig={viewabilityConfig}
onViewableItemsChanged={onViewableItemsChanged}
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
flatlistContainer: {
height: height * 0.45,
},
});
Demo
Take a look at the Intersection Observer API documentation which is an implementation you can use to detect when an element is visible on the screen or not.
Here's a very simple example where the green div is "observed". Whether it is visible or not is marked in state.
Here's a working sandbox
package.json
{
"name": "react18-intersection-observer",
"version": "1.0.0",
"description": "Detect visible screen elemenet using intersection observer",
"keywords": [
"react",
"starter"
],
"main": "src/index.js",
"dependencies": {
"react": "18.2.0",
"react-bootstrap": "^2.7.1",
"react-dom": "18.2.0",
"react-scripts": "^5.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
],
"author": "Wesley LeMahieu"
}
index.js
import { createRoot } from "react-dom/client";
import App from "./App";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(<App />);
app.js
import { useEffect, useRef, useState } from "react";
const App = () => {
const observer = useRef(null);
const observerRef = useRef(null);
const [isVisible, setIsVisible] = useState(false);
const observerCallback = async (e) => {
if (e.length) {
setIsVisible(e[0].isIntersecting);
} else {
setIsVisible(false);
}
};
useEffect(() => {
if (observerRef.current) {
if (observer.current) {
observer.current.disconnect();
observer.current.observe(observerRef.current);
} else {
observer.current = new IntersectionObserver(observerCallback);
observer.current.observe(observerRef.current);
}
}
return () => observer.current.disconnect();
}, []);
useEffect(() => {
if (isVisible) {
alert("Green box visible");
} else {
alert("Grey box visible");
}
console.log(isVisible ? "GREEN BOX VISIBLE" : "GREEN BOX NOT VISIBLE");
}, [isVisible]);
return (
<div style={{ display: "flex" }}>
<div
style={{
backgroundColor: isVisible ? "green" : "red",
width: "10%",
height: "3000px",
}}
>
Visible
</div>
<div style={{ width: "90%" }}>
<div
style={{ backgroundColor: "grey", opacity: 0.6, height: "1000px" }}
>
Other div
</div>
<div
ref={observerRef}
style={{ backgroundColor: "green", opacity: 0.6, height: "1000px" }}
>
Observed Div
</div>
<div
style={{ backgroundColor: "grey", opacity: 0.6, height: "1000px" }}
>
Other div
</div>
</div>
</div>
);
};
export default App;

React Native - searchApi is not a function

I am new in React Native. I try to create a simple searching food restaurant with Yelp. Unfortunately, I get an error:
"searchApi is not a function. (in 'searchApi(term)', 'searchApi' is
"")
Below my code.
useResults.js
import React, { useEffect, useState } from 'react';
import yelp from '../api/yelp';
export default () => {
const [result, setResult] = useState([]);
const [errorMessage, setErrorMessage] = useState('');
const searchApi = async (searchTerm) => {
console.log("hi there");
try {
const response = await yelp.get('/search', {
params: {
limit: 50,
term: searchTerm,
location: 'san jose'
}
});
setErrorMessage(null);
setResult(response.data.businesses);
} catch (err) {
setErrorMessage('Something Went Wrong');
}
};
/*
useEffect(() => {}); //Run the arrow function everytime the component is rendered
useEffect(() => {}, []); // Run the arrow function only when the component is first rendered
useEffect(() => {}, [value]); // Run the arrow function only when the component is first rendered, and when the value is changes
*/
useEffect(() => {
searchApi('pasta');
}, []);
return [searchApi, result, errorMessage];
};
SearchScreen.js
import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import ResultList from '../components/ResultList';
import SearchBar from '../components/SearchBar';
import useResults from '../hooks/useResults';
const SearchScreen = () => {
const [term, setTerm] = useState('');
const [searchApi, result, errorMessage] = useResults();
console.log(result);
return (
<View>
<SearchBar
term={term}
onTermChange={setTerm}
onTermSubmit={() => searchApi(term)}
/>
<View>{errorMessage ? <Text>{errorMessage}</Text> : null}</View>
<Text>We have found {result.length} results</Text>
<ResultList title="Cost Effective" />
<ResultList title="Bit Pricier" />
<ResultList title="Big Spender"/>
</View>
);
};
const styles = StyleSheet.create({
});
export default SearchScreen;
edit :
SearchBar.js
import React from 'react';
import { View, Text, StyleSheet, TextInput } from 'react-native';
import { Feather } from '#expo/vector-icons';
const SearchBar = ({ term, onTermChange, onTermSubmit }) => {
return (
<View style={styles.backgroundStyle}>
<Feather style={styles.iconStyle} name="search" size={30} color="black" />
<TextInput style={styles.inputStyle}
autoCapitalize="none"
autoCorrect={false}
placeholder="Search"
value={term}
onChangeText={onTermChange}
onEndEditing={onTermSubmit}
/>
</View>
)
};
const styles = StyleSheet.create({
backgroundStyle: {
marginTop: 10,
backgroundColor: '#F0EEEE',
height: 50,
borderRadius: 5,
marginHorizontal: 15,
flexDirection: 'row'
},
inputStyle: {
flex: 1,
fontSize: 18,
marginHorizontal: 10
},
iconStyle: {
fontSize: 35,
alignSelf: 'center'
}
});
export default SearchBar;
When I type in search bar and hit done button, I got the error above.
Seems in useResults.js file this: return [searchApi, result, errorMessage]; does not properly return the function. But the result and errorMessage return successfully.
And in this file: SearchScreen.js the error line is shown in here: onTermSubmit={() => searchApi(term)}.
How to fix this?
Try adding a callback to onChangeText.
<TextInput style={styles.inputStyle}
autoCapitalize="none"
autoCorrect={false}
placeholder="Search"
value={term}
onChangeText={() => onTermChange()} // Add fat arrow function here
onEndEditing={onTermSubmit}
/>

FaceDetector: does not work option faceDetectionClassifications.all

I’m trying to get the information from smilingProbability, leftEyeOpenProbabilitye rightEyeOpenProbability, which in the documentation just says that the option ‘faceDetectionClassifications’ should be selected as ‘all’, but in my code it is exactly as requested and even so it doesn’t work.
See my code:
import React, { useState, useEffect } from "react";
import { StyleSheet, Text, View, SafeAreaView } from "react-native";
import { Camera } from "expo-camera";
import * as FaceDetector from "expo-face-detector";
export default function App() {
const [hasPermission, setHasPermission] = useState(null);
const [faces, setFaces] = useState([]);
const faceDetected = ({ faces }) => {
setFaces(faces);
console.log({ faces }); // ou (faces)
};
useEffect(() => {
(async () => {
const { status } = await Camera.requestPermissionsAsync();
setHasPermission(status === "granted");
})();
}, []);
if (hasPermission !== true) {
return <Text>No access to camera</Text>;
}
return (
<SafeAreaView style={styles.container}>
<Camera
style={{ flex: 1 }}
type="front"
onFacesDetected={faceDetected}
FaceDetectorSettings={{
mode: FaceDetector.Constants.Mode.fast,
detectLandmarks: FaceDetector.Constants.Landmarks.all,
runClassifications: FaceDetector.Constants.Classifications.all,
minDetectionInterval: 10000,
tracking: false,
}}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
},
});
link on GitHub: https://github.com/JoyceQuerubino/FaceDetector

Function inside reanimated call() is not getting updated after props update

in my react-native project, I'm trying to use a callback when the State of LongPressGestureHandler becomes END | FAILED | CANCELLED. The function for this callback uses a global variable reactions that I receive as props. Even after this prop gets updated. The function somehow is using the old reactions. I'll be really thankful if you could explain what I'm doing wrong.
`
import React, { useState, useEffect } from 'react';
import { useNavigation } from '#react-navigation/native';
import { LongPressGestureHandler, State } from 'react-native-gesture-handler';
import { Vibration, View, Text } from 'react-native';
import Animated from 'react-native-reanimated';
import { useGestureHandler } from 'react-native-redash';
import useReaction from '../../../../../../services/dashboard/feed/components/reaction/api';
import assets from '../../../../../../assets';
import styles from './styles';
const {
Value, useCode, block, eq, call, onChange, cond, and, or, set
} = Animated;
export default ({
reactions,
emoji,
componentRefetch,
loggedInUsersId,
itemId,
counts,
}) => {
const { navigate } = useNavigation();
const [giveReaction, giveUnReaction] = useReaction(componentRefetch);
const reacted = reactions.find(
(reaction) => (reaction.user ? reaction.user.id === loggedInUsersId : false)
&& reaction.emoji === emoji
);
const [popAnim] = useState(new Value(1));
const onEmojiPress = async () => {
console.log('Emoji Pressed');
console.log(reactions.length);
if (reacted) {
await giveUnReaction(itemId, emoji, reacted.id);
} else {
await giveReaction(itemId, emoji);
}
};
const onEmojiLongPress = () => {
console.log('Emoji long Pressed');
Vibration.vibrate(1);
navigate(assets.strings.dashboard.feeds.reactantsPopup.NAME, {
reactions,
emoji,
});
};
const longPressState = new Value(State.UNDETERMINED);
const longPressGestureHandler = useGestureHandler({ state: longPressState });
const shouldScale = new Value(-1);
useCode(
() => block([
cond(
eq(longPressState, State.BEGAN),
set(shouldScale, 1)
),
cond(
eq(longPressState, State.FAILED),
call([], onEmojiPress)
),
cond(
or(
eq(longPressState, State.CANCELLED),
eq(longPressState, State.END),
),
call([], onEmojiLongPress)
),
]),
[]
);
return (
<LongPressGestureHandler {...longPressGestureHandler} minDurationMs={100}>
<Animated.View
style={{
...styles.icon,
borderColor: reacted
? assets.colors.appDefaults.primaryColor
: '#eeeeee',
backgroundColor: reacted
? assets.colors.appDefaults.primaryColorLight
: '#eeeeee',
}}
>
<Animated.View
style={{
transform: [
{ scale: popAnim },
{
translateY: popAnim.interpolate({
inputRange: [1, 1.3],
outputRange: [0, -2],
}),
},
],
}}
>
<Text
style={{
color: reacted ? assets.colors.appDefaults.primaryColor : 'black',
}}
>
{`${emoji ?? '👍'} `}
</Text>
</Animated.View>
<Text
style={{
color: reacted ? assets.colors.appDefaults.primaryColor : 'black',
}}
>
{`${counts[emoji]}`}
</Text>
</Animated.View>
</LongPressGestureHandler>
);
};
`
I found out that(by experimenting) useCode sends a set of instructions to native side, in order to change the set of instructions to latest prop update, we need to fire the useCode on prop update.
useCode(, [dependencyThatGetsUpdated])

How to hide components when keyboard is up?

Is there a way to hide components when the keyboard shows, aside from installing packages?
Using the code sample from the Keyboard documentation, I would do something like this:
class Example extends Component {
state = {keyboardOpen: false};
componentDidMount() {
this.keyboardDidShowListener = Keyboard.addListener(
'keyboardDidShow',
this._keyboardDidShow,
);
this.keyboardDidHideListener = Keyboard.addListener(
'keyboardDidHide',
this._keyboardDidHide,
);
}
componentWillUnmount() {
this.keyboardDidShowListener.remove();
this.keyboardDidHideListener.remove();
}
_keyboardDidShow() {
this.setState({
keyboardOpen: true
});
}
_keyboardDidHide() {
this.setState({
keyboardOpen: false
});
}
render() {
return (
{!this.state.keyboardOpen && <MyComponent />}
);
}
}
Basically, in componentDidMount you subscribe to the show and hide keyboard events. You keep track of the Keyboard current state in your Component state thanks to this.state.keyboardOpen and you conditionally display MyComponent based on the value of this.state.keyboardOpen in your render method.
For those using functional components, here is a hook that you could use to detect when the keyboard is opened and closed.
import { useEffect, useState, useMemo } from "react";
import { Keyboard } from "react-native";
const useKeyboardOpen = () => {
const [isKeyboardOpen, setIsKeyboardOpen] = useState(false);
useEffect(() => {
const keyboardOpenListener = Keyboard.addListener("keyboardDidShow", () =>
setIsKeyboardOpen(true)
);
const keyboardCloseListener = Keyboard.addListener("keyboardDidHide", () =>
setIsKeyboardOpen(false)
);
return () => {
if (keyboardOpenListener) keyboardOpenListener.remove();
if (keyboardCloseListener) keyboardCloseListener.remove();
};
}, []);
return isKeyboardOpen;
};
export default useKeyboardOpen;
And this is how I use it in my projects:
import { useTheme } from "react-native-paper";
import useKeyboardOpen from "hooks/useKeyboardOpen";
export const Login = () => {
const theme = useTheme();
const isKeyboardOpen = useKeyboardOpen();
const styles = useStyles(theme, isKeyboardOpen);
return (
<View style = {styles.container}>
<View style = {styles.logo}>
<Logo />
</View>
<View style = {styles.form}>
....
</View>
</View>
);
};
const useStyles = (theme, isKeyboardOpen) = (
StyleSheet.create({
container: {
flex: 1,
},
logo: {
flex: 1,
marginTop: 20,
justifyContent: "center",
alignItems: "center",
...(isKeyboardOpen && {
display: "none",
}),
},
form: {
flex: 1,
}
})
);
I made this into a npm package if anyone is interested.
https://github.com/TIKramer/react-native-hide-onkeyboard
You can hide the view by using HideOnKeyboard> </HideOnKeyboard