React native DrawerLayoutAndroid and Switch component doesnt work well - react-native

I have a switch component on DrawerLayoutAndroid component. When I drag the switch component, drawer component is being slided.
How do I ensure when i drag switch, drawer panel is in blocked mode.
[EDIT]
Attaching the mocks below
Mocks

Usually a tap on the Switch will toggle the switch state. If you want to make it drag as well and not let the other view become the responder, you can do that by implementing GestureResponseHandler. Something like this.
const RespondHandler = {
onStartShouldSetResponder: function (e) {
return true;
},
onResponderGrant: function (e) {
return true;
}
};
<Switch {...RespondHandler}/>

Related

How unmount a hook after going to new screen with navigate

The context is a simple React Native app with React Navigation.
There are 3 screens.
The first simply displays a button to go to second screen using navigation.navigate("SecondScreen").
The Second contains a hook (see code below) that adds a listener to listen the mouse position. This hook adds the listener in a useEffect hook and removes the listener in the useEffect cleanup function. I just added a console.log in the listener function to see when the function is triggered.
This screen contains also a button to navigate to the Third screen, that only shows a text.
If I go from first screen to second screen: listener in hook start running. Good.
If I go back to the first screen using default react navigation 's back button in header. the listener stops. Good.
If I go again to second screen, then listener runs again. Good.
But if I now go from second screen to third screen, the listener is still running. Not Good.
How can I unmount the hook when going to third screen, and mount it again when going back to second screen?
Please read the following before answering :
I know that:
this is due to the fact that react navigation kills second screen when we go back to first screen, and then trigger the cleanup function returned by the useEffect in the hook. And that it doesn't kill second screen when we navigate to third screen, and then doesn't trigger the cleanup function.
the react navigation's hook useFocusEffect could be used to resolve this kind of problem. But it can't be used here because it will involve to replace the useEffect in the hook by the useFocusEffect. And I want my hook to be usable in every context, even if react navigation is not installed. More, I'm using here a custom hook for explanation, but it's the same problem for any hook (for example, the native useWindowDimensions).
Then does anyone know how I could manage this case to avoid to have the listener running on third screen ?
This is the code of the hook sample, that I take from https://github.com/rehooks/window-mouse-position/blob/master/index.js, but any hook could be used.
"use strict";
let { useState, useEffect } = require("react");
function useWindowMousePosition() {
let [WindowMousePosition, setWindowMousePosition] = useState({
x: null,
y: null
});
function handleMouseMove(e) {
console.log("handleMouseMove");
setWindowMousePosition({
x: e.pageX,
y: e.pageY
});
}
useEffect(() => {
window.addEventListener("mousemove", handleMouseMove);
return () => {
window.removeEventListener("mousemove", handleMouseMove);
};
}, []);
return WindowMousePosition;
}
module.exports = useWindowMousePosition;
the react navigation's hook useFocusEffect could be used to resolve this kind of problem. But it can't be used here because it will involve to replace the useEffect in the hook by the useFocusEffect. And I want my hook to be usable in every context, even if react navigation is not installed
So your hook somehow needs to know about the navigation state. If you can't use useFocusEffect, you'll need to pass the information about whether the screen is focused or not (e.g. with an enabled prop).
function useWindowMousePosition({ enabled = true } = {}) {
let [WindowMousePosition, setWindowMousePosition] = useState({
x: null,
y: null
});
useEffect(() => {
if (!enabled) {
return;
}
function handleMouseMove(e) {
console.log("handleMouseMove");
setWindowMousePosition({
x: e.pageX,
y: e.pageY
});
}
window.addEventListener("mousemove", handleMouseMove);
return () => {
window.removeEventListener("mousemove", handleMouseMove);
};
}, [enabled]);
return WindowMousePosition;
}
And then pass enabled based on screen focus:
const isFocused = useIsFocused();
const windowMousePosition = useWindowMousePosition({ enabled: isFocused });
Note that this approach will need the screen to re-render when it's blurred/focused unlike useFocusEffect.

How to close a nativescript modal after a few navigations?

A modal is opened, which has multiple pages. For the navigation I'm using frame navigation. There is a close button on every page clicking on which closes the modal.
What I'm doing now is passing this.$modal as a property to each page which creates a long chain of property passing and on each page I just do this.modal.close() where this.modal is the property of the component that refers to the this.$modal of the first page.
I was wondering if there was a better way, such as accessing the topmost open modal and closing it.
I'm using nativescript-vue and the builtin nativescript modals
Please note that I have multiple modals in other parts of my application. there is only this one that has navigation in it.
A small improvement could be saving the modal into the Vuex store, accessing it anytime instead of chaining props.
Detach modal component by plugin.
const modalDialog = {
install (Vue, options = {}) {
// ...
}
}
Vue.use(modalDialog)
Designate Vue prototype for plugin.
const modalDialog = {
install (Vue, options = {}) {
Vue.prototype.$modal = {
show () {
// ...
},
hide () {
// ..
}
}
}
}
Vue.use(modalDialog)
this.$modal is accessible from all components.
this.$modal.show() // or hide()

React-Native Tab Navigator memory leak due to no component willunmount

I am really confused how to solve the issue of canceling an async process when moving to a new tab. If you start an async request on a page but, then navigate to a new tab before it's complete, you will get the warning: "Can't call setState (or forceUpdate) on an unmounted component"
However, changing screens via the tab navigator will never fire the willunmount so, there is no real place to cancel any operations.
Stack Navigator and switch navigator fire this and I can cancel any operations just fine. I literally am about to build my own bottom nav to get around this.
This sample is way to hacky IMHO:
YES, I've tried the this.isMounted approach (BTW you now will get the isMounted(...) is deprecated warning if you use that) Yes, I've used the willupdate method but, PureComponent is suppose to remove that "hack".
This really feels like a bug to me and I am at lost to how to have a bottom Navigation AND have a page with some fetch results.
// Hacky Example in async method
try {
let response = await fetch(
'https://your/rest/endpoint/with/json'
);
if (response.ok) {
if (!_isMounted) {
console.log('oops! ' + SCREEN_NAME + ' was unmounted before async');
return; // just bail if component is no longer mounted
}
let responseJson = await response.json();
`
If you're using Redux:
You could move all of your Async Code into an Action ...
If you're executing your async code in an action instead of executing it inside your component's life ... it's ok if the user decided to move to a different tab and the action has not been resolved yet ((because it's running in a different context than the component's lifecycle)) ... once the action is done and your component is still active >> then it'll receive a new set of props to update itself ...
And regarding setting state on an unmounted component ... you could use this template for any class-based component that has a state:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
// other fields...
isUnmounted: false,
};
}
componentWillUnmount() {
this.setState({ isUnmounted: true });
}
setComponentState = (values) => {
if (!this.state.isUnmounted) this.setState(values);
};
}
Redux is not wrong but, for my case more complicated than required.
So, if anyone is struggling with React-Navigation Tab Navigator and async fetch. I was able to achieve the pattern I was after (firing an event to be able to cancel async events).
You CAN add a Listener when the screen is navigated to or from (blur)
React Navigation emits events to screen components that subscribe to them:
willFocus - the screen will focus
didFocus - the screen focused (if there was a transition, the transition completed)
willBlur - the screen will be unfocused
didBlur - the screen unfocused (if there was a transition, the transition completed)
basically I did this:
async componentDidMount() {
console.log(SCREEN_NAME + ' Component Did Mount');
this.props.navigation.addListener('willBlur', (route) => {
_isMounted = false;
this.axiosCancelSource.cancel('Component unmounted.');
});
// didFocus will fire on 1st Mount as well
this.props.navigation.addListener('didFocus', async (route) => {
_isMounted = true;
this.axiosCancelSource = axios.CancelToken.source();
await this._getTourList();
});
console.log(SCREEN_NAME + ' Component Did Mount Complete');
}
React-Navtive Navigation props web site

Keyboard Handling in React Native

How to make your app respond gracefully on keyboard appearance?
So far I have tried keyboard-aware-scroll, keyboardspacer and keyboard Avoiding view
Keyboard avoiding view didn't help at all I have tried it several times but it doesn't even respond to keyboard appearance.
Keyboardspacer gracefully works but in many cases it destroys the whole UI by crushing other view
keyboardaware scroll works when there is no scroll in the app but for long forms it doesn't work.
android:windowSoftInputMode="adjustPan" only works for android
What are the other options that we have for the app to gracefully respond when keyboard appears.
What do you use in your apps?
If none of these libraries does what you need, you can adjust your view manually by using the Keyboard module (docs at https://facebook.github.io/react-native/docs/keyboard)
With it you can react when you know a keyboard opens or closes, like so:
import * as React from 'react';
import { Keyboard } from 'react-native';
class MyComponent extends React.Component {
componentDidMount() {
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this.keyboardDidHide);
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this.keyboardDidShow);
}
componentWillUnmount() {
this.keyboardDidHideListener.remove();
this.keyboardDidShowListener.remove();
}
keyboardDidShow = () => {
//Fix your view for when a keyboard shows
};
keyboardDidHide = () => {
//Fix your view for when a keyboard hides
};
//Rest of component...
}
For my projects I use react-native-keyboard-aware-scroll-view as well as KeyboardAvoidingView (try to play with behavior prop, it depends on your styling).
Take a look in Android configuration section in docs of react-native-keyboard-aware-scroll-view. I think it's something that you're looking for.
You can find following usefull answer related your question.
Q.How to change the Softkeyboard “Enter” button Text in android?
https://stackoverflow.com/a/53098939/6477946
Q. How to close or hide SoftKeyBoard
https://stackoverflow.com/a/53077131/6477946

NavigatorIOS - Is there a viewDidAppear or viewWillAppear equivalent?

I'm working on porting an app to React-Native to test it out. When I pop back to a previous view in the navigator stack (hit the back button) I'd like to run some code. Is there a viewWillAppear method? I see on the Navigator there is a "onDidFocus()" callback which sounds like it might be the right thing.. but there doesn't appear to be anything like that on NavigatorIOS
I find a way to simulate viewDidAppear and viewDidDisappear in UIKit,
but i'm not sure if it's a "right" way.
componentDidMount: function() {
// your code here
var currentRoute = this.props.navigator.navigationContext.currentRoute;
this.props.navigator.navigationContext.addListener('didfocus', (event) => {
//didfocus emit in componentDidMount
if (currentRoute === event.data.route) {
console.log("me didAppear");
} else {
console.log("me didDisappear, other didAppear");
}
console.log(event.data.route);
});
},
For people who are using hooks and react navigation version 5.x, I think you can do this to expect similar behavior of viewDidAppear:
import React, {useCallback } from "react";
import { useFocusEffect } from "#react-navigation/native";
const SomeComponent = () => {
useFocusEffect(
useCallback(() => {
//View did appear
}, [])
);
//Other codes
}
For more information, refer https://reactnavigation.org/docs/use-focus-effect/
Here is a solution to simulate viewDidAppear with latest React Navigation version:
componentDidMount() {
var currentRoute = this.props.navigation.state.routeName;
this.props.navigation.addListener('didFocus', (event) => {
if (currentRoute === event.state.routeName) {
// VIEW DID APPEAR
}
});
}
Thanks Jichao Wu for the idea :)
If you are using React Navigation, use this:
componentDidMount(){
this.props.navigation.addListener('focus', () => {
// put your code here
});
}
Basically you are adding a focus event when component is first mounted. It will be called whenever (including the first time too) the component is focused. Ideally you'd also need to remove listener on unmount by capturing the value returned from addListener call and call that returned value (which is actually the unsubscribe function).
I've created a custom button with onLeftButtonPress to handled the back to run code as per https://github.com/facebook/react-native/issues/26
The way to get around it is to either set your custom back button on the left side, or to implement - viewWillDisappear: in iOS.
You can use ComponentWillMount or if you're leaving the view you can use ComponentWillUnmount which will run some code on exit.