PanGestureHandler state change interrupt gesture - reactjs

I am using PanGestureHandler from react-native-gesture-handler, everything is working fine but when the state is changed inside onGestureEvent, the PanGestureHandler stop working and I have to release my finger and touch again to start the gesture. Please check the code below that describe my problem:
import React, {useState} from 'react';
import {View, Text} from 'react-native';
import {PanGestureHandler, State} from 'react-native-gesture-handler';
import FastImage from 'react-native-fast-image';
import styles from '../Styles/HomeStyles.js';
const PanResp = () => {
const [testState, setTestState] = useState('State....');
const gestureEventHandler = ({nativeEvent}) => {
if (nativeEvent.x + nativeEvent.translationX <= 74) {
setTestState('New State');
} else {
console.log('X Position => ', nativeEvent.x);
}
};
const onDragRelease = () => {
console.log('Drag Release');
};
return (
<View>
<PanGestureHandler
onGestureEvent={gestureEventHandler}
onEnded={onDragRelease}>
<FastImage
style={styles.imgIcon}
source={{uri: 'http://i.imgur.com/LwCYmcM.gif'}}
/>
<FastImage
style={styles.imgIcon}
source={{uri: 'http://i.imgur.com/k5jMsaH.gif'}}
/>
</PanGestureHandler>
</View>
);
};
export default PanResp;
Note: The above codes work fine, and when the state update and I release my fingers from touch no DragRelease event is firing, and also the PanGestureHandler not respond to touch any more. I am stuck at this point from past week.

Related

How can I press individual option in actionsheet in React Native?

I am still new to React Native. I have an actionsheet with two options and a cancel option. I am having trouble understanding how to make each option do something different when pressed.
My code:
import React, { useRef } from "react"
import ActionSheet from 'react-native-actionsheet'
import { View, Text, Pressable } from "react-native";
import Icon from 'react-native-vector-icons/FontAwesome';
const ActionSheet = () => {
let actionSheet = useRef();
let optionArray = ['Orange', 'Cherry', 'Cancel'];
const showActionSheet = () => {
actionSheet.current.show();
}
return (
<View
<Pressable onPress={showActionSheet}>
<View>
<Text>Choose Fruit</Text>
<Icon name="angle-right" size={15}/>
</View>
</Pressable>
<ActionSheet
ref={actionSheet}
options={optionArray}
cancelButtonIndex={2}
onPress={{????}}
/>
</View>
);
};
What I'd like to do is navigate to a different screen when an option is pressed
Would appreciate any help. Thank you in advance!
The onPress function provides an index argument. Thus consider the following code snippet.
const onActionSelect = (index) => {
if (index === 1) {
// first action pressed
} else if (index === 2) {
// second action pressed
}
// and so on
}
<ActionSheet
ref={actionSheet}
options={optionArray}
cancelButtonIndex={2}
onPress={onActionSelect}
/>

My react hooks fail when used inside my react component that is deeply nesed into my react application

When I use useHistory hook here ...
import React from 'react';
import {View,Image,StyleSheet, TouchableOpacity} from 'react-native';
import theme from '../theme';
import Text from './Text'
import {useQuery} from '#apollo/react-hooks'
import {GET_REPOSITORY} from '../graphql/queries'
import {useHistory} from 'react-router-native'
const RepositoryItem = (props) => {
let history = useHistory()
let item = props.item
const styles = StyleSheet.create({
...
});
const reduceToK = (num) => {
if (num > 999) {
const thousandsNum = Math.round(num / 1000);
const remainerNum = Math.round((num % 1000) / 100);
return `${thousandsNum}.${remainerNum}k`;
} else return num;
};
return (
<View style={styles.rootContainer}>
{...}
</View>
);
};
export default RepositoryItem;
The application breaks and returns the following error ...
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 1. You might have mismatching versions of React and the renderer (such as React DOM)2. You might be breaking the Rules of Hooks3. You might have more than one copy of React in the same app
This component (RepositoryItem) is being used inside another component (RepositoryList) like so ...
import React from 'react';
import { FlatList, View, StyleSheet, Text } from 'react-native';
import RepositoryItem from './RepositoryItem';
// ...
const styles = StyleSheet.create({
...
});
const RepositoryList = () => {
let history = useHistory()
const repositories = useQuery(GET_REPOSITORIES, {fetchPolicy: 'cache-and-network'});
if (!repositories.loading) {
const ItemSeparator = () => <View style={styles.separator} />;
// Get the nodes from the edges array
const repositoryNodes = repositories.data.repositories && !repositories.loading
? repositories.data.repositories.edges.map(edge => edge.node)
: [];
return (
<FlatList
data={repositoryNodes}
ItemSeparatorComponent={ItemSeparator}
renderItem={RepositoryItem}
keyExtractor={repository => repository.fullName}
/>
);
} else {
return (<View><Text>Loading...</Text></View>);
}
};
export default RepositoryList;
That component is being used in the App component.
My theory is that in react native's FlatList renderItem prop one cannot use hooks.
Any idea what the issue might be here?
You may want to do it this way because renderItem accepts a function.
<FlatList
data={repositoryNodes}
ItemSeparatorComponent={ItemSeparator}
renderItem={({item}) => <RepositoryItem item={item} />}
keyExtractor={repository => repository.fullName}
/>
change
import {useHistory} from 'react-router-native'
t0
import {useHistory} from 'react-router-dom'
based on react-router-native documentation

React Native Navigation: Check if drawer is opened or not

i have a component that is outside the Drawer tag but it's inside a NavigationContainer.
So i used useRef from react to get navigate method .
So i use this example: Navigating without navigation prop
But now, i can't get the state of the drawer (is it opened or closed).
Here's my App component:
import 'react-native-gesture-handler';
import React from 'react';
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { Provider } from "react-redux";
import GetInformationByLocation from "./reducers/GetInformationByLocation";
import Wrapper from './components/Wrapper';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { NavigationContainer } from '#react-navigation/native';
import GeoDisplay from "./components/GeoDisplay";
import { Text } from "react-native";
import { navigationRef } from "./helpers/Navigator";
const store = createStore(GetInformationByLocation, applyMiddleware(thunk));
/*
* TODO:
* 1. Wrap everything with parent component
* 2. Search bar
* 3. Save searched cities.
* 4. Check if the city is undefined and suggest nearest city.
* 5. Animated background
* 6. Fancy and animated font
* 7. Setup menu преди показване на приложението
*/
const Drawer = createDrawerNavigator();
const App = () => {
return (
<Provider store={store}>
<NavigationContainer ref={navigationRef}>
<Wrapper />
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={GeoDisplay} />
</Drawer.Navigator>
</NavigationContainer>
</Provider >
);
}
export default App;
and my Wrapper.js Component:
import React, { useEffect, useState } from 'react';
import { StyleSheet, View, StatusBar } from 'react-native';
import { SearchBar, Header } from 'react-native-elements';
import { MainUI } from '../styling/UI';
import * as Navigator from "../helpers/Navigator";
import { DrawerActions } from "#react-navigation/native";
const Wrapper = (props) => {
const [search, setSearch] = useState(true);
const openDrawer = () => {
Navigator.navigationRef.current.dispatch(DrawerActions.openDrawer())
setSearch(false);
}
useEffect(() => {
}, []);
return (
<>
<StatusBar backgroundColor={"#011E25"} />
<Header
leftComponent={{ icon: 'menu', color: '#fff', onPress: () => { openDrawer() } }}
centerComponent={{ text: 'COVID Bulgaria', style: { color: '#fff' } }}
rightComponent={{ icon: 'home', color: '#fff' }}
backgroundColor={'#011E25'}
/>
{search &&
<View>
<SearchBar placeholder="Търси град" containerStyle={MainUI.searchContainer} />
</View>
}
</>
)
}
export default Wrapper;
I dispatch openDrawer() action with navigationRef
Navigator.navigationRef.current.dispatch(DrawerActions.openDrawer())
but can't get status of the drawer.
I've tried many ways but not work.
Thanks in advance.
You can check if drawer is open by getting the navigation state:
const state = navigationRef.current.getRootState();
const isDrawerOpen = state.history.some((it) => it.type === 'drawer');
The above code assumes that drawer is at root. If it's nested, you'll need to traverse the state to find the state object with type: 'drawer'.
It's not clear why you need to check it from the question. Normally you shouldn't need to check it. If you dispatch DrawerActions.openDrawer() if drawer is already open, nothing will happen. So the check is unnecessary. If you want to close the drawer if it was open, you can dispatch DrawerActions.toggleDrawer() instead.
You can create a helper class like:
class DrawerHandler() {
openDrawer() {
this.drawerOpened = true;
}
closeDrawer() {
this.drawerOpened = false;
}
getDrawerStatus() {
return this.drawerOpened;
}
}
export default new DrawerHandler();
Use open and close functions on drawer opening and closing and get function to get status of the drawer

React Native save change switch with Async Storage

Im new in React Native, I have a problem with Switch, I want to save changes, dark mode and Switch, when I turn off the app and come back my changes should be saved. When I close the app, my switch came back to first position and dark mode does not work. I know that Im doing something wrong, but I did not mobile app and this is my first time and I dont know how to use AsyncStorage in this App to work this. Can somebody help me solve this problem?
import React, { createContext, useState, useEffect } from 'react';
import { AsyncStorage } from 'react-native';
export const DarkModeContext = createContext();
export default function DarkModeContextProvider(props) {
const [switchMode, setSwitchMode] = useState(false);
useEffect(() => {
let switch1 = switchMode;
AsyncStorage.setItem('switch1', JSON.stringify(switch1));
});
const SwitchThis = () => {
setSwitchMode(!switchMode);
};
return (
<DarkModeContext.Provider
value={{
switchMode,
SwitchThis
}}
>
{props.children}
</DarkModeContext.Provider>
);
}
and next component:
import React, { useState, useContext } from 'react';
import { View, ScrollView, TouchableOpacity, Text, AsyncStorage } from 'react-native';
import { List } from 'react-native-paper';
import BackgroundImage from './BackgroundImage';
import Clock from './Clock';
import TabIcon from './TabIcon';
import AddButton from './AddButton';
import { DarkModeContext } from './app-context';
const HomeScreen = () => {
const { switchMode } = useContext(DarkModeContext);
displayData = async () => {
try {
let switch1 = await AsyncStorage.getItem('switch1', function (err, switch1) {
JSON.parse(switch1)
}
)
return switch1
}
catch (error) {
return error
}
}
return (
<View
style={{
flex: 1,
backgroundColor: !switchMode ? 'white' : '#353535'
}}
>
<BackgroundImage fabButton={<AddButton/>}>
<Clock />
</BackgroundImage>
<ScrollView>
<List.Section>
<List.Subheader style={{ color: !switchMode ? 'black' : 'white' }}>
Task List
</List.Subheader>
<TouchableOpacity onPress={displayData}>
<Text>Click displayData</Text>
</TouchableOpacity>
</List.Section>
</ScrollView>
</View>
);
};
You are importing AsyncStorage from 'react-native' which is deprecated
use #react-native-community/react-native-async-storage
npm i #react-native-community/react-native-async-storage
And on your home screen you are not calling the function displayData() so how is data supposed to be displayed without function call.
and i do suggest making separate functions for writing and reading from async storage, it will help you reduce your code and time.
Like this:
let storeData=(name, obj)=> {
return new Promise((resolve,reject)=>{
let jsonOfItem = JSON.stringify(obj)
AsyncStorage.setItem(name, jsonOfItem).then(r=>resolve(jsonOfItem))
.catch(e=>reject(e))
})
}
let readData=(name)=> {
return new Promise((resolve,reject)=>{
//we want to wait for the Promise returned by AsyncStorage.setItem()
//to be resolved to the actual value before returning the value
AsyncStorage.getItem(name).then(r=> resolve(JSON.parse(r)) )
.catch(e=>reject(e))
})
}
//Now you can just read write easily in async function like this:
let fetchedData = await readData("key")
//and for storing data.
storeData("key",object)

Navigate to completely different screen on React Native

On my React Native app I am initially loading SceneA, which simply displays a text "Click me". When this button is clicked I would like to load a completely different screen (SceneB) that has completely different components.
All the examples that I found online (like the example below) load a scene with exactly the same components, but different values. In my case it is a completely different layout. How can I navigate to that new screen (SceneB)?
It is basically the equivalent to a new Activity on Android, passing some data at the same time. Any pointers will be appreciated.
index.android.js
import React, { Component } from 'react';
import { AppRegistry, Navigator } from 'react-native';
import SceneA from './SceneA';
class ReactNativeTest extends Component {
render() {
return (
<Navigator
initialRoute={{ title: 'My Initial Scene', index: 0 }}
renderScene={(route, navigator) =>
<SceneA
title={route.title}
// Function to call when a new scene should be displayed
loadNewScene={() => {
const nextIndex = route.index + 1;
navigator.push({
title: 'Scene B',
index: nextIndex,
});
}}
/>
}
/>
)
}
}
AppRegistry.registerComponent('ReactNativeTest', () => ReactNativeTest);
SceneA.js
import React, { Component, PropTypes } from 'react';
import { View, Text, TouchableHighlight } from 'react-native';
export default class SceneA extends Component {
render() {
return (
<View>
<Text>Scene A</Text>
<TouchableHighlight onPress={this.props.loadNewScene}>
<Text>Click Me</Text>
</TouchableHighlight>
</View>
)
}
}
SceneA.propTypes = {
loadNewScene: PropTypes.func.isRequired,
};
You handle which components should render in the renderScene function, each scene will have a route.title so you can decide which to render based on that.
renderScene={(route, navigator) =>
if (route.title === 'Scene A') {
return <SceneA navigator={navigator} />
}
if (route.title === 'Scene B') {
return <SceneB navigator={navigator} />
}
}
Then inside your components you're gonna have a function that handles the navigation:
navigateToSceneB = () => {
this.props.navigator.push({
title: 'Scene B'
})
}

Resources