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
Related
I'm using #expo/react-native-action-sheet, and i want to show a button in the props message.
e.g
import { useActionSheet } from "#expo/react-native-action-sheet"
const { showActionSheetWithOptions } = useActionSheet()
const onPress = () => {
// Here
**const message = '<TouchableOpacity><Text>fsdf</Text></TouchableOpacity>'**
showActionSheetWithOptions(
{
message
},
(buttonIndex) => {
}
)
}
But it is not showing the button as i want
My purpose is to add a date picker in the action sheet.
Expecting answer:
In this case, you can use another library https://gorhom.github.io/react-native-bottom-sheet/ because Action Sheet is about the list of actions.
You can place any content you need for react-native-bottom-sheet and it also supports Expo
import React, { useCallback, useMemo, useRef } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import BottomSheet from '#gorhom/bottom-sheet';
const App = () => {
// ref
const bottomSheetRef = useRef<BottomSheet>(null);
// variables
const snapPoints = useMemo(() => ['25%', '50%'], []);
// callbacks
const handleSheetChanges = useCallback((index: number) => {
console.log('handleSheetChanges', index);
}, []);
// renders
return (
<View style={styles.container}>
<BottomSheet
ref={bottomSheetRef}
index={1}
snapPoints={snapPoints}
onChange={handleSheetChanges}
>
<View style={styles.contentContainer}>
<Text>Awesome 🎉</Text>
</View>
</BottomSheet>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 24,
backgroundColor: 'grey',
},
contentContainer: {
flex: 1,
alignItems: 'center',
},
});
export default App;
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.
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
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;
In my react-native application, it gives me an error, saying ": In this environment the sources for assign MUST be an object. This error is a performance optimization and not spec compliant" when I try to set this.props.navigation.setParams({});
What is the reason for this error?
import React, { Component } from "react";
import { View, Text, ScrollView, StyleSheet } from "react-native";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import {
Workout,
WorkoutDetail,
KeyValueText,
DetailText
} from "../../components";
import { getWorkout } from "../../actions";
class WorkoutDetailScreen extends Component {
constructor(props) {
super(props);
this.state = {
currentWorkout: {}
};
}
componentDidMount() {
const workoutId = this.props.navigation.state.params;
this.props.getWorkout(workoutId);
this.props.navigation.setParams({}); // If I remove this line, the error goes. But I want to set params here.
}
render() {
let currentWorkout = this.props.currentWorkout;
let tools =
currentWorkout.tools && currentWorkout.tools.length > 0
? currentWorkout.tools.join(", ")
: "Not Required";
let muscles = currentWorkout.muscles
? currentWorkout.muscles.join(", ")
: "";
return (
<ScrollView style={styles.container}>
<WorkoutDetail
workout={this.props.currentWorkout}
workoutImage={currentWorkout.workoutImage}
onPressWorkout={() => alert("CONTINUE WORKOUT")}
/>
<View style={styles.workoutInfo}>
<KeyValueText header="Time" value={currentWorkout.length} />
<KeyValueText header="Difficulty" value={currentWorkout.difficulty} />
<KeyValueText header="Tools" value={tools} />
<KeyValueText header="Muscles" value={muscles} />
</View>
<View>
<DetailText text={currentWorkout.description} />
</View>
</ScrollView>
);
}
// navigation options
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
headerTitle: "TITLE",
headerTitleStyle: {
width: "100%",
alignItems: "center"
},
headerStyle: {
paddingRight: 10,
paddingLeft: 10
}
};
};
}
// styles
const styles = StyleSheet.create({
container: {
flex: 1
},
workoutInfo: {
paddingBottom: 10,
borderBottomWidth: 1,
borderBottomColor: "gray"
}
});
const mapStateToProps = state => {
return {
currentWorkout: state.workouts.currentWorkout
};
};
const mapDispatchToProps = dispatch => {
return {
getWorkout: bindActionCreators(getWorkout, dispatch)
};
};
export default connect(mapStateToProps, mapDispatchToProps)(
WorkoutDetailScreen
);
This is probably because you're using an empty object in your setParams call. React navigation then probably uses Object.assign to set your navigation parameters and it hits this error
Try reseting your navigation params by providing an object that represents the initial state and not an empty object.