Action dispatched and state updated, but no render of the UI component - react-native

Who can support on my issue: Dispatching an action does does change the state as anticipated. But the component from which the issue gets dispatched does not re-render. When I simply save the component, it of course re-renders and shows the desiree effekt.
Here my files:
actions.js
export const TOGGLE_PRODUCT = "TOGGLE_PRODUCT";
export const INCREMENT = "INCREMENT";
//ACTION CREATER FUNCTIONS
export const toggleProduct = (id) => {
return {
type: TOGGLE_PRODUCT,
productId: id,
};
};
reducer.js
import { PRODUCTLIST } from "../../data/dummydata";
import { TOGGLE_PRODUCT } from "../actions/products";
const initialState = {
allProducts: PRODUCTLIST,
};
const productReducer = (state = initialState, action) => {
switch (action.type) {
case TOGGLE_PRODUCT:
const Products = state.allProducts;
const toggledProduct = Products.find((el) => el.id === action.productId);
if (toggledProduct.status === false) {
toggledProduct.status = true;
} else {
toggledProduct.status = false;
}
console.log("Neue Products: ", Products);
return {
allProducts: Products,
};
default:
return state;
}
};
export default productReducer;
component.js
import { useSelector, useDispatch } from "react-redux";
import React, { useEffect, useCallback } from "react";
import { Text, View, Button, FlatList, StyleSheet } from "react-native";
import Product from "../components/Product";
import { toggleProduct } from "../store/actions/products";
import { increment } from "../store/actions/products";
const ShoppingListScreen = (props) => {
const dispatch = useDispatch();
const toggleProductHandler = useCallback(
// useCallback verhindert infinite loop
(id) => {
dispatch(toggleProduct(id));
},
[]
);
const Products = useSelector((state) => state.product.allProducts);
return (
<View style={styles.screen}>
<FlatList
data={Products}
renderItem={({ item }) => (
<View
style={
item.status === true ? styles.elementselected : styles.element
}
>
<Product
style={styles.text}
id={item.id}
product={item.product}
department={item.department}
status={item.status}
onClick={() => toggleProductHandler(item.id)}
/>
</View>
)}
/>
<View style={styles.button}>
<Button
title="FERTIG"
onPress={() => {
props.navigation.goBack();
}}
/>
{/* <Button
title='Stand "cartRewe" '
onPress={() => {
props.testFunction1();
}}
/>
<Button
title='Stand "planRewe" '
onPress={() => {
props.testFunction2();
}}
/> */}
</View>
</View>
);
};
const styles = StyleSheet.create({
screen: {
backgroundColor: "#fafafa",
flex: 1,
justifyContent: "flex-start",
},
element: {
backgroundColor: "#ddd",
borderWidth: 2,
borderColor: "#bbb",
borderRadius: 20,
marginVertical: 5,
marginHorizontal: 25,
},
elementselected: {
backgroundColor: "#a0ffa0",
borderWidth: 3,
borderColor: "#64ff64",
borderRadius: 20,
marginVertical: 5,
marginHorizontal: 25,
},
text: {
color: "#333",
// fontSize: 22,
// marginHorizontal: 10
},
button: {
marginVertical: 24,
},
});
export default ShoppingListScreen;

These lines are the problem:
const Products = state.allProducts;
const toggledProduct = Products.find((el) => el.id === action.productId);
if (toggledProduct.status === false) {
toggledProduct.status = true;
} else {
toggledProduct.status = false;
}
return {
allProducts: Products,
};
That's mutating the existing state, and you must never mutate the state in a Redux reducer!.
To fix this, you would need to make copies of the Products array and the toggledProduct object, update the copies, and return those.
Having said that, the better option is to use our official Redux Toolkit package, which is our recommended approach for writing Redux logic. It lets you write "mutating" logic that is turned into safe and correct immutable updates.
Here's what this would look like with Redux Toolkit's createSlice API:
const productsSlice = createSlice({
name: 'products',
initialState: {allProducts: []},
reducers: {
productToggled(state, action) {
const toggledProduct = state.allProducts.find(e => e.id === action.payload);
// This "mutating" syntax _only_ works inside Redux Toolkit's createSlice/createReducer!
toggledProduct.status = !toggledProduct.status;
}
}
})

This totally makes sense to not mutate the state object. Since I do now want to implement a further package just for this issue, I revised my source code as advised. For any reason my component still does not rerender without a manual safe. How about this code, what is still wrong?
reducer.js
import { PRODUCTLIST } from "../../data/dummydata";
import { TOGGLE_PRODUCT } from "../actions/products";
const initialState = {
allProducts: PRODUCTLIST,
};
const productReducer = (state = initialState, action) => {
switch (action.type) {
case TOGGLE_PRODUCT:
const newProducts = state.allProducts;
const toggledProduct = newProducts.findIndex(
(el, idx) => el.id === action.productId
);
if (newProducts[toggledProduct].status === false) {
newProducts[toggledProduct].status = true;
} else {
newProducts[toggledProduct].status = false;
}
return {
...state,
newProducts,
};
default:
return state;
}
};
export default productReducer;

Related

how to use react native redux in react navigation

I am using redux persist with asyncstorage save items in a bookmarks list. The items are in a flatlist and when I click on one item, it navigates me to another screen. I would like to implement the functional bookmark in the header of that screen.
When I tried doing this, and clicked the bookmark in the header, and go back to the bookmarks, it just shows a blank card. It looks like it is not updating the state properly. How can I fix this?
StackNavigator.tsx
const MainStackNavigator = () => {
const { books, bookmarks } = useAppSelector((state) => state.booksReducer);
const dispatch = useDispatch();
const fetchBooks = () => dispatch(getBooks());
const addToBookmarkList = (book) => dispatch(addBookmark(book));
const removeFromBookmarkList = (book) => dispatch(removeBookmark(book));
useEffect(() => {
fetchBooks();
}, []);
const handleAddBookmark = (book) => {
addToBookmarkList(book);
};
const handleRemoveBookmark = (book) => {
removeFromBookmarkList(book);
};
const handleSwapBookmark = (book) => {
removeFromBookmarkList(book);
};
const RenderItem = () => {
const ifExists = (book) => {
if (bookmarks.filter((item) => item.id === book.id).length > 0) {
return true;
}
return false;
};
return (
<TouchableOpacity
onPress={() =>
ifExists(i) ? handleRemoveBookmark(i) : handleAddBookmark(i)
}
activeOpacity={0.7}
style={{
flexDirection: "row",
padding: 2,
backgroundColor: ifExists(i) ? "#F96D41" : "#2D3038",
borderRadius: 20,
alignItems: "center",
justifyContent: "center",
height: 40,
width: 40,
}}
>
<MaterialCommunityIcons
color={ifExists(i) ? "white" : "#64676D"}
size={24}
name={ifExists(i) ? "bookmark-outline" : "bookmark"}
/>
</TouchableOpacity>
);
};
return (
<AppStack.Navigator>
<AppStack.Screen
name="BookmarksScreen"
component={BookmarksScreen}
options={{
title: "Search",
statusBarColor: isDarkMode ? "white" : "black",
headerLargeTitle: true,
headerTranslucent: true,
headerLargeTitleHideShadow: true,
}}
/>
<AppStack.Screen
name="Screen2"
component={Screen2}
options={({ route }) => ({
headerLargeTitle: false,
title: route.params.name,
headerTranslucent: true,
headerRight: () => <RenderItem item={route.params.name} />,
})}
/>
</AppStack.Navigator>
);
};
actions.js
import axios from "axios";
import { BASE_URL } from "../config";
// Define action types
export const GET_BOOKS = "GET_BOOKS";
export const ADD_TO_BOOKMARK_LIST = "ADD_TO_BOOKMARK_LIST";
export const REMOVE_FROM_BOOKMARK_LIST = "REMOVE_FROM_BOOKMARK_LIST";
export const SWAP_IN_BOOKMARK_LIST = "SWAP_IN_BOOKMARK_LIST";
export const getBooks = () => {
try {
return async (dispatch) => {
const response = await axios.get(`${BASE_URL}`);
if (response.data) {
dispatch({
type: GET_BOOKS,
payload: response.data,
});
} else {
console.log("Unable to fetch data from the API BASE URL!");
}
};
} catch (error) {
console.log(error);
}
};
export const addBookmark = (book) => (dispatch) => {
dispatch({
type: ADD_TO_BOOKMARK_LIST,
payload: book,
});
};
export const removeBookmark = (book) => (dispatch) => {
dispatch({
type: REMOVE_FROM_BOOKMARK_LIST,
payload: book,
});
};
hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./store";
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
reducers.ts
import {
GET_BOOKS,
ADD_TO_BOOKMARK_LIST,
REMOVE_FROM_BOOKMARK_LIST,
} from "./actions";
const initialState = {
books: [],
bookmarks: [],
};
function booksReducer(state = initialState, action) {
switch (action.type) {
case GET_BOOKS:
return { ...state, books: action.payload };
case ADD_TO_BOOKMARK_LIST:
return { ...state, bookmarks: [...state.bookmarks, action.payload] };
case REMOVE_FROM_BOOKMARK_LIST:
return {
...state,
bookmarks: state.bookmarks.filter(
(book) => book.id !== action.payload.id
),
};
default:
return state;
}
}
export default booksReducer;
store.ts
import { createStore, combineReducers, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import AsyncStorage from "#react-native-async-storage/async-storage";
import { persistStore, persistReducer } from "redux-persist";
import booksReducer from "./reducers";
const persistConfig = {
key: "root",
storage: AsyncStorage,
whitelist: ["bookmarks"],
};
const rootReducer = combineReducers({
booksReducer: persistReducer(persistConfig, booksReducer),
});
export const store = createStore(rootReducer, applyMiddleware(thunk));
export const persistor = persistStore(store);
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
BookmarksScreen.tsx
const BookmarksScreen = () => {
return (
<View>
<FlatList
data={bookmarks}
keyExtractor={(item) => item.id}
renderItem={renderItem}
showsVerticalScrollIndicator={false}
/>
</View>
);
}
renderItem
const renderItem = ({ item }) => {
return (
<TouchableOpacity
onPress={() =>
navigation.navigate("Screen2", {name: item.name})}
>
<View style={{ flexDirection: "row", flex: 1 }}>
<View>
<Text
style={{
fontSize: 22,
paddingRight: 16,
color: "black",
fontFamily: "Medium",
left: 45,
top: 6,
}}
>
{item.country}
</Text>
</View>
</View>
</View>
</TouchableOpacity>
);
};
I think you forgot to access props that's the reason booked-marked not working i change some code please check it's working or not.
StackNavigator.tsx
const RenderItem = (item) => {
const ifExists = (book) => {
if (bookmarks.filter((item) => item.id === book.id).length > 0) {
return true;
}
return false;
};
return (
<TouchableOpacity
onPress={() =>
ifExists(item) ? handleRemoveBookmark(item) : handleAddBookmark(item)
}
activeOpacity={0.7}
style={{
flexDirection: "row",
padding: 2,
backgroundColor: ifExists(i) ? "#F96D41" : "#2D3038",
borderRadius: 20,
alignItems: "center",
justifyContent: "center",
height: 40,
width: 40,
}}
>
<MaterialCommunityIcons
color={ifExists(i) ? "white" : "#64676D"}
size={24}
name={ifExists(i) ? "bookmark-outline" : "bookmark"}
/>
</TouchableOpacity>
);
};

React Native Modal not displaying using context and redux

I created a custom ModalContext.js but my test modal does not seem to be showing when I trigger a button's onPress
// ModalContext.js
import createDataContext from './createDataContext';
const modalReducer = (state, action) => {
switch (action.type) {
case 'openModal':
return { ...state, component: action.payload };
case 'hideModal':
return { ...state, component: null };
default:
return state;
}
};
const openModal = (dispatch) => (component) => {
console.log('hey there');
dispatch({ type: 'openModal', payload: component });
};
const hideModal = (dispatch) => () => {
dispatch({ type: 'hideModal' });
};
export const { Provider, Context } = createDataContext(
modalReducer,
{
openModal,
hideModal,
},
{ component: null }
);
// createDataContext.js
import React, { useReducer } from 'react';
export default (reducer, actions, defaultValue) => {
const Context = React.createContext();
const Provider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, defaultValue);
const boundActions = {};
for (let key in actions) {
boundActions[key] = actions[key](dispatch);
}
return (
<Context.Provider value={{ state, ...boundActions }}>
{children}
</Context.Provider>
);
};
return { Context, Provider };
};
// App.js
const App = createAppContainer(navigator);
export default () => {
return (
<ModalProvider>
<AuthProvider>
<App
ref={(navigator) => {
setNavigator(navigator);
}}
/>
</AuthProvider>
</ModalProvider>
);
};
I have a button on a test screen to check if it works or not.
// WebViewScreen.js
import React, { useState, useContext } from 'react';
import { StyleSheet, Modal, View, Text, Button } from 'react-native';
import { Context as ModalContext } from '../context/ModalContext';
const WebViewScreen = ({ navigation }) => {
const { state, openModal } = useContext(ModalContext);
const errorModal = (
<View>
<Modal animationType='slide' visible={true}>
<View style={styles.centeredView}>
<View style={styles.modalView}>
<Text>Hello</Text>
</View>
</View>
</Modal>
</View>
);
return (
<>
<Button
onPress={() => {
openModal(errorModal);
}}
title='button'
/>
</>
);
};
WebViewScreen.navigationOptions = ({ navigation }) => {
return {
title: navigation.getParam('title'),
};
};
const styles = StyleSheet.create({
view: {
backgroundColor: '#f1f3f4',
},
centeredView: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
marginTop: 22,
},
modalView: {
margin: 20,
backgroundColor: 'white',
borderRadius: 20,
padding: 35,
alignItems: 'center',
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
});
export default WebViewScreen;
It seems to actually call the function as I can see "hey there" on the console but no modals appear.
I am not sure this is what you are looking for or not but I manage my details like following.
class Abc {
var1;
var2;
var3 = new B();
var4 = new C();
var5 = {}
var6 = [];
success = a => {
//your logic
}
}
class B {
varA;
varB;
varC = false;
}
I hope this is helpful.

React Native Calendars : How to change the background color of only one item using Redux

I'm trying to change the background of only one item in this agenda but with my code, when I click on an item, it changes the background color of all of them and not just the one I clicked : Before clicked after clicked. I want also to store the value of my state in redux so that I can display the screen elsewhere in my application while conserving the green color.
Here is my code from my calendar:
import React, {useState, useCallback, useEffect} from 'react';
import {
Text,
View,
TouchableOpacity,
} from 'react-native';
import { useSelector, useDispatch } from "react-redux";
import { Avatar, Card } from 'react-native-paper';
import {Agenda} from "react-native-calendars";
import { toggleActive} from "../store/actions/calendar";
import * as firebase from "firebase";
const activeItemText = 'Réservé';
const activeItemStyles = {
backgroundColor: 'lightgreen',
backgroundColor2: 'white',
};
const inactiveItemText = 'Disponible';
const inactiveItemStyles = {
backgroundColorPrimary: 'white',
backgroundColorSecondary: 'white',
};
const timeToString = (time) => {
const date = new Date(time);
return date.toISOString().split('T')[0];
};
const CalendarItem = ({item, firstItemInDay}) => {
// const [active, setActive] = useState(false);
const dispatch = useDispatch();
const active = useSelector(state => state.calendar.active);
const toggleActiveHandler = useCallback(() => {
dispatch(toggleActive())
}, [dispatch]);
// const [active1, setActive1] = React.useState(false);
// console.log(active);
/* const changeColor = () => {
setActive(!active)
};
*/
return (
<TouchableOpacity
style={{marginTop: 17, marginRight: 10}}
onPress={toggleActiveHandler}>
<Card style={active ? activeItemStyles : inactiveItemStyles}>
<Card.Content>
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
}}>
<Text>{active ? activeItemText : inactiveItemText}</Text>
<Avatar.Text label="J" />
</View>
</Card.Content>
</Card>
</TouchableOpacity>
);
};
const renderItem = (item, firstItemInDay) => {
return <CalendarItem item={item} firstItemInDay={firstItemInDay} />;
};
const calendarScreen = () => {
const [items, setItems] = useState({});
const loadItems = (day) => {
setTimeout(() => {
for (let i = -15; i < 85; i++) {
const time = day.timestamp + i * 24 * 60 * 60 * 1000;
const strTime = timeToString(time);
if (!items[strTime]) {
items[strTime] = [];
const numItems = 1;
for (let j = 0; j < numItems; j++) {
items[strTime].push({
name: inactiveItemStyles.texte,
height: Math.max(50, Math.floor(Math.random() * 150)),
});
}
}
}
const newItems = {};
Object.keys(items).forEach((key) => {
newItems[key] = items[key];
});
setItems(newItems);
}, 1000);
};
return (
<View style={{flex: 1}}>
<Agenda
items={items}
loadItemsForMonth={loadItems}
selected={'2020-09-23'}
renderItem={renderItem}
/>
</View>
);
};
export default calendarScreen;
Here is my reducer :
import { TOGGLE_ACTIVE } from "../actions/calendar";
const initialState = {
active: false
};
const calendarReducer = (state = initialState, action) => {
switch (action.type) {
case TOGGLE_ACTIVE :
console.log(state);
console.log(!!state);
return {
active: !!state};
default:
return state
}
};
export default calendarReducer;
And here is my action :
export const TOGGLE_ACTIVE = 'TOGGLE_ACTIVE';
export const toggleActive = () => {
return { type: TOGGLE_ACTIVE }
};
Thanks a lot in advance for your help !
You are just saving active state, that's why its changing background for all. Cause, it can't differentiate for which you have changed the state . So, keep some unique data from the item that you have clicked to compare it with others. If the redux data match change background otherwise not.

React Native net-info

I'm trying to add an offline notification bar into my app, I have the following code that is called from my App.js.
import React, { PureComponent } from 'react';
import { View, Text, Dimensions, StyleSheet,Alert } from 'react-native';
import NetInfo from "#react-native-community/netinfo";
const dimensions = Dimensions.get('window');
let outofaction = 1;
NetInfo.fetch().then(state => {
console.log("Connection type", state.type);
console.log("Is connected?", state.isConnected);
if (state.isConnected == false) {
outofaction = 0;
} else {
outofaction = 1;
}
});
//class OfflineNotice extends PureComponent {
const OfflineNotice = () => {
NetInfo.fetch().then(state => {
console.log("Connection type", state.type);
console.log("Is connected?", state.isConnected);
if (state.isConnected == false) {
outofaction = 0;
} else {
outofaction = 1;
}
});
// Subscribe
const unsubscribe = NetInfo.addEventListener(state => {
console.log("Connection type", state.type);
console.log("Is connected?", state.isConnected);
if (state.isConnected == false) {
outofaction = 0;
} else {
outofaction = 1;
}
});
// Unsubscribe
unsubscribe();
function MiniOfflineSign() {
if (outofaction == 0) {
return (
<View style={styles.offlineContainer}>
<Text style={styles.offlineText}>Offline</Text>
</View>
);
} else {
return (
<View style={styles.offlineContainer}>
<Text style={styles.offlineText}>Online</Text>
</View>
);
}
}
return (
<MiniOfflineSign />
)};
const styles = StyleSheet.create({
offlineContainer: {
backgroundColor: '#b52424',
height: 30,
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'row',
width: dimensions.width,
position: 'absolute',
top:40,
zIndex:1
},
offlineText: { color: '#fff' }
});
export default OfflineNotice;
The code works partially. I start Online, then turn off my internet on my laptop, and if I refresh / reload, then it will show offline.
Two problems I have;
I want it to update in near real-time when the isConnected changes (this doesn't appear to be happening)
It doesn't get stuck on one state (though item 1 above would fix that)
I made something similar, this might help you with things.
NoInternetMessageBar component
// NoInternetMessageBar.js
import Netinfo from '#react-native-community/netinfo';
import React, { useContext, useEffect } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Store } from 'store/Store';
export default function NoInternetMessageBar() {
const {
state,
actions: { networkConnectionChanged },
} = useContext(Store);
useEffect(() => {
const unsubscribe = Netinfo.addEventListener(({ isConnected }) => networkConnectionChanged(isConnected));
return () => {
unsubscribe();
};
}, []);
if (state.isConnected) return null;
return (
<View style={styles.container}>
<Text style={styles.message}>Cannot reach internet</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
position: 'absolute',
bottom: 0,
height: 40,
width: '100%',
backgroundColor: 'gray',
},
message: {
color: 'white',
marginLeft: 20,
},
});
action
const networkConnectionChanged = async isConnected => {
return dispatch({
type: NETWORK_CHANGED,
payload: isConnected,
});
};
reducer
case NETWORK_CHANGED:
return {
...state,
isConnected: action.payload,
};
On top level use it like this.
<>
<AppContainer />
<NoInternetMessageBar />
</>
You are unsubscribing from NetInfo updates immediately after subscribing to them. Functional components are just that - functions. The code you wrote in there will be executed every time that component renders.
You should instead put your subscribe/unsubscribe in a useEffect hook, so that you subscribe on mount and unsubscribe on unmount.
useEffect(() => {
const unsubscribe = NetInfo.addEventListener(state => {
...
});
return () => {
unsubscribe();
}
}, []);
Subscribe in componentDidMount() and Unsubscribe in componentWillUnmount(), like this -
class A extends React.Component {
constructor() {
this.unsubscribe;
}
componentDidMount(){
this.unsubscribe = NetInfo.addEventListener(...);
}
componentWillUnmount() {
this.unsubscribe && this.unsubscribe();
}
}

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