I am using react-native and I want to count the steps of the user like Samsung Health step indicator.
I am using react-native-sensors library to access accelerometer data.
I followed this tutorial for pedometer algorithm, implemented in react-native.
import {View, Text} from 'react-native';
import {
accelerometer,
SensorTypes,
setUpdateIntervalForType,
} from 'react-native-sensors';
setUpdateIntervalForType(SensorTypes.accelerometer, 400);
const App = () => {
const [xAcceleration, setXAcceleration] = useState(0);
const [yAcceleration, setYAcceleration] = useState(0);
const [zAcceleration, setZAcceleration] = useState(0);
const [magnitudePrevious, setMagnitudePrevious] = useState(0);
const [steps, setSteps] = useState(0);
useEffect(() => {
const subscription = accelerometer
.pipe(data => data)
.subscribe(speed => {
setXAcceleration(speed.x);
setYAcceleration(speed.y);
setZAcceleration(speed.z);
});
return () => {
subscription.unsubscribe();
};
}, []);
useEffect(() => {
const magnitude = Math.sqrt(
Math.pow(xAcceleration, 2) +
Math.pow(yAcceleration, 2) +
Math.pow(zAcceleration, 2),
);
const magnitudeDelta = magnitude - magnitudePrevious;
setMagnitudePrevious(() => magnitude);
// I tried magnitudeDelta > 6, magnitudeDelta > 4,
// magnitudeDelta > 2, magnitudeDelta > 10 but didn't work properly
if (magnitudeDelta > 2) setSteps(prevSteps => prevSteps + 1);
}, [xAcceleration, yAcceleration, zAcceleration]);
return (
<View>
<Text>{steps}</Text>
</View>
)
}
If a shake my phone upwards or sideways it increments the steps but I think it's ok because samething happens in Samsung Health. But the main problem is it is not as accurate as Samsung Health when you walk.
For example: If a stepped 20 times it only counts 8 of them. I want it to be close to actual value.
I am new to react-native.
I am making a project in react-native with my android phone connected to my laptop at the USB port. When I run the project I see a blank screen on my phone. My phone is android version 9.
First I ran npm start and then I ran npm run android.
I ran adb -s device-name reverse tcp:8081 tcp:8081. Still the screen on my phone is blank.
This is my App.tsx file:
import "./i18n"
import React, { useState, useEffect, useRef } from "react"
import { YellowBox } from "react-native"
import { NavigationContainerRef } from "#react-navigation/native";
import { contains } from "ramda"
import { enableScreens } from "react-native-screens"
import { SafeAreaProvider, initialWindowSafeAreaInsets } from "react-native-safe-area-context";
import { RootNavigator, exitRoutes, setRootNavigation } from "./navigation"
import { useBackButtonHandler } from "./navigation/use-back-button-handler"
import { RootStore, RootStoreProvider, setupRootStore } from "./models/root-store"
import * as storage from "./utils/storage"
import getActiveRouteName from "./navigation/get-active-routename"
import { ThemeProvider } from 'react-native-elements';
import * as theme from 'theme';
import { AuthScreensWelcomeScreen } from './screens/auth-screens/welcome-screen';
enableScreens()
YellowBox.ignoreWarnings([
"componentWillMount is deprecated",
"componentWillReceiveProps is deprecated",
"Require cycle:",
])
const canExit = (routeName: string) => contains(routeName, exitRoutes)
export const NAVIGATION_PERSISTENCE_KEY = "NAVIGATION_STATE"
const App: React.FunctionComponent<{}> = () => {
const navigationRef = useRef<NavigationContainerRef>()
const [rootStore, setRootStore] = useState<RootStore | undefined>(undefined)
const [initialNavigationState, setInitialNavigationState] = useState()
const [isRestoringNavigationState, setIsRestoringNavigationState] = useState(true)
setRootNavigation(navigationRef)
useBackButtonHandler(navigationRef, canExit)
const routeNameRef = useRef()
const onNavigationStateChange = state => {
const previousRouteName = routeNameRef.current
const currentRouteName = getActiveRouteName(state)
if (previousRouteName !== currentRouteName) {
// track screens.
__DEV__ && console.tron.log(currentRouteName)
}
// Save the current route name for later comparision
routeNameRef.current = currentRouteName
// Clear the storage if we are navigating to auth stack
if ( ['register', 'login', 'forgotpassword'].includes(currentRouteName) ) {
storage.remove(NAVIGATION_PERSISTENCE_KEY);
} else {
// Persist navigation state to storage
storage.save(NAVIGATION_PERSISTENCE_KEY, state)
}
}
useEffect(() => {
(async () => {
setupRootStore().then(setRootStore)
})()
}, [])
useEffect(() => {
const restoreState = async () => {
try {
const state = await storage.load(NAVIGATION_PERSISTENCE_KEY)
if (state) {
setInitialNavigationState(state)
}
} finally {
setIsRestoringNavigationState(false)
}
}
if (isRestoringNavigationState) {
restoreState()
}
}, [isRestoringNavigationState])
if (!rootStore) {
return null
}
return (
<SafeAreaProvider initialSafeAreaInsets={initialWindowSafeAreaInsets}>
<AuthScreensWelcomeScreen />
</SafeAreaProvider>
)
}
export default App
Here is the welcome screen that should appear:
import React, { FunctionComponent, useState } from "react"
import { observer } from "mobx-react-lite"
import { Dimensions, Image } from "react-native"
import { Screen, Button } from "components"
import Carousel, { Pagination } from 'react-native-snap-carousel';
import { Block } from "components/block/block"
const { width: ScreenWidth } = Dimensions.get('screen');
import { NavigationProp, NavigationState } from "#react-navigation/native";
export interface AuthScreensWelcomeScreenProps {
navigation?: NavigationProp<Record<string, object>, string, NavigationState,
{}, {}>
}
export const AuthScreensWelcomeScreen:
FunctionComponent<AuthScreensWelcomeScreenProps> = observer((props) => {
const {
navigation
} = props;
const [activeSlide, setActiveSlide ] = useState(0);
const items = [
{ image: require('../../../assets/splash1.jpg') },
{ image: require('../../../assets/splash2.jpg') },
{ image: require('../../../assets/splash3.jpg') }
];
function renderItem ( { item, index } ) {
return (
<Image style={{ alignSelf: 'center', flex: 1, resizeMode: 'contain', width: ScreenWidth / 1.25 }} source={item.image}></Image>
)
}
return (
<Screen preset="fixed">
<Block flex>
<Carousel
sliderWidth={ScreenWidth}
itemWidth={ScreenWidth}
data={ items }
layout="default"
renderItem={ renderItem }
onSnapToItem={ index => { setActiveSlide( index ) } }/>
<Block>
<Pagination
activeDotIndex={activeSlide}
dotsLength={items.length}
dotStyle={{
width: 10,
height: 10
}}/>
</Block>
</Block>
<Block row my={20} space="evenly" px={20}>
<Button title="Continue" containerStyle={{ flex: 1 }} onPress={ ( ) => navigation.navigate('login') }/>
</Block>
</Screen>
)
})
Can anyone help?
If rootStore does not exist, then your render method returns null, so the page will be blank. Try this:
if (!rootStore) {
console.log('rootStore is not found');
return null
}
console.log('continue to render');
If you do not see continue to render printed out in your Metro server window, then that indicates rootStore was not found. Then you need to figure out why it doesn't exist, or at least show some kind of informative view to handle that case.
I replaced this:
<RootStoreProvider value={rootStore}>
<SafeAreaProvider initialSafeAreaInsets={initialWindowSafeAreaInsets}>
<ThemeProvider theme={theme}>
<RootNavigator
ref={navigationRef}
initialState={initialNavigationState}
onStateChange={onNavigationStateChange}
/>
</ThemeProvider>
</SafeAreaProvider>
</RootStoreProvider>
with
<SafeAreaProvider initialSafeAreaInsets={initialWindowSafeAreaInsets}>
<AuthScreensWelcomeScreen />
</SafeAreaProvider>
to get the blank white screen to go away. Please tell me if this is the correct way to do it.
I want the name of the current route or screen in react-navigation which I want to use inside if condition to make some changes.
For react-navigation v5:
import { useRoute } from '#react-navigation/native';
const route = useRoute();
console.log(route.name);
You can catch it as the following code:
this.props.navigation.state.routeName
If you are using nested navigators, you can use this code to get current active screen's state
import { NavigationState } from 'react-navigation';
const getActiveRouteState = function (route: NavigationState): NavigationState {
if (!route.routes || route.routes.length === 0 || route.index >= route.routes.length) {
return route;
}
const childActiveRoute = route.routes[route.index] as NavigationState;
return getActiveRouteState(childActiveRoute);
}
Usage:
const activeRoute = getActiveRouteState(this.props.navigation.state);
I'm using this when I need to get current active screen's state from NavigationDrawer.
This works fine in react-navigation v5.x
this.props.route.name
const routeNameRef = React.createRef();
<NavigationContainer
ref={navigationRef}
onReady={() => routeNameRef.current = navigationRef.current.getCurrentRoute().name}
onStateChange={() => {
const previousRouteName = routeNameRef.current
const currentRouteName = navigationRef.current.getCurrentRoute().name
if (previousRouteName !== currentRouteName) {
// Do something here with it
}
// Save the current route name for later comparision
routeNameRef.current = currentRouteName
}}
>
{/* ... */}
</NavigationContainer>
);
export function getCurrentRouteName(action) {
return routeNameRef;
}
You can import the function getCurrentRouteName and use this to get the current route name and its working in any nested navigators in React Navigation 5.
While using "react-navigation": "^3.0.8" and DrawerNavigator it can be accessed from the this.props object using
this.props.activeItemKey
Preparation
register 🔗NavigationService.js,see the doc detail in Navigating without the navigation prop
<App
ref={navigatorRef => {
NavigationService.setTopLevelNavigator(navigatorRef);
}}
/>
recursion function
function getCurrentRoute(nav){
if(Array.isArray(nav.routes)&&nav.routes.length>0){
return getCurrentRoute(nav.routes[nav.index])
}else {
return nav.routeName
}
}
get current routeName
getCurrentRoute(NavigationService.getNavigator().state.nav)
In React Navigation v5, I was able to pull the current route name with the below approach:
import { useNavigationState } from '#react-navigation/native'
const routes = useNavigationState(state => state.routes)
const currentRoute = routes[routes.length -1].name
console.log('currentRoute: ',currentRoute)
It is possible to get this from the navigationRef attached to the navigation container. Where navigationRef is a ref.
export const navigationRef = React.createRef()
<NavigationContainer
ref={navigationRef}
>
<Navigator />
</NavigationContainer>
Then use: const currentRouteName = navigationRef.current.getCurrentRoute().name
Alternatively in a functional component you can useRef const navigationRef = React.useRef()
For react-navigation v5, you could use the useNavigationState hook -
import {useNavigationState} from '#react-navigation/native';
const state = useNavigationState(state => state);
const routeName = (state.routeNames[state.index]);
console.log(routeName);
import {getFocusedRouteNameFromRoute,useRoute} from '#react-navigation/native';
//...
const route = useRoute();
const routeName = getFocusedRouteNameFromRoute(route); // Get Nested Route Name
With version 5.x the best way currently is getFocusedRouteNameFromRoute
import { getFocusedRouteNameFromRoute } from '#react-navigation/native';
export default function Stack(route) {
// If the focused route is not found, we need to assume it's the initial screen
// This can happen during if there hasn't been any navigation inside the screen
// In our case, it's "Feed" as that's the first screen inside the navigator
const routeName = getFocusedRouteNameFromRoute(route) ?? 'Feed';
return <> ..... </>
}
import { useNavigation } from '#react-navigation/native';
const App = () => {
const navigation = useNavigation();
const { dangerouslyGetState } = useNavigation();
const { index, routes } = dangerouslyGetState()
console.log(routes[index].name);
return(
<>
</>
)
};
You can use this in hooks as well.
console.log(navigation.dangerouslyGetState());
this.props.navigation.state.routeName works only in react-navigation 4 but react-navigation 5 doesn't support it.
The current route name can be achieved by using redux:
-Navigator component passes route object as a prop to the child component
-The Child component receives props and could find the route name in route.name
-To get updated route name on the screen change you can use focus event listener on navigation
<====== Parent Component where navigation is implemented ======>
import React from "react";
import { createMaterialTopTabNavigator } from "#react-navigation/material-top-
tabs";
import ChildScreen from "../screens/Home/childScreen";
const Tab = createMaterialTopTabNavigator();
const ParentNavigations = () => {
return (
<Tab.Navigator
>
<Tab.Screen name="ChildScreen" component={ChildScreen} />
</Tab.Navigator>
);
};
export default ParentNavigations;
<===== Child component =====>
import React, { useEffect } from "react";
import { View, StyleSheet } from "react-native";
import { useDispatch } from "react-redux";
import ActionTypes from "../../store/actions/ActionsTypes";
const ChildScreen = ({ navigation, route }) => {
const dispatch = useDispatch();
useEffect(() => {
const unsubscribe = navigation.addListener("focus", () => {
dispatch({ type: ActionTypes.SETROUTE, payload: route.name }); // every time when screen gets focued it will update the route through redux
});
return unsubscribe;
}, [navigation, route]);
return (
<View style={styles.container}>
<Text>Hello</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#0C0B0B",
},
});
export default ChildScreen;
If you just want to see if the current screen is focused, you can use navigation.isFocused().
https://reactnavigation.org/docs/navigation-prop/#isfocused
Example:
_backAction() {
const { navigation } = this.props;
if (navigation.isFocused()) {
this.setState({
isLeavingApp: true,
});
}
}
const Home = ({ navigation, route }) => {
// you will see screen key, name and params
console.log("ROUTE", route);
// rest of your code
};
For react native navigation 5.x use :
props.state.routeNames[props.state.index]
In one line with useNavigationState Hook:
const screenName = useNavigationState((state) => state.routes[state.index].name)
If you are using React Navigation v6 you can use this:
import { useRoute } from '#react-navigation/native';
...
const route = useRoute();
console.log('Current Route: ', route.name);
And if you want to get the name of the screen that you are, and you are inside a nested navigator, you can do this:
import { useNavigationState } from '#react-navigation/native';
...
const routes = useNavigationState(state => state.routes);
const currentRouteIndex =
routes?.length && routes[routes.length - 1].state?.index;
const currentRoute =
routes[routes.length - 1].state?.routeNames[currentRouteIndex];
console.log('Current Route: ', currentRoute);
This simple code worked for me. Just add this function to your Util.ts/js file and from your component pass the navigation as the object.
export const getCurrentScreenName = (navigation: any) => {
return navigation.getState().routes[navigation.getState().index].name;
};
This is step by step procedure of what Justin.Mathew has described in his answer.
Create a new file called RootNavigation.js and put the below content inside.
// RootNavigation.js
import * as React from 'react';
export const navigationRef = React.createRef(); // we will access all navigation props by importing this in any of the component
Now import the navigationRef from the RootNavigation.js file, and assign NavigationContainer ref to this. After this step navigationRef can function as navigation prop globally.
// App.js
import { NavigationContainer } from '#react-navigation/native';
import { navigationRef } from './RootNavigation';
export default function App() {
handleNavigationRef = (ref) => {
// DON'T DO navigationRef = ref, cause this will give you "navigationRef is
// read only" error.
navigationRef.current = ref;
}
return (
<NavigationContainer ref={handleNavigationRef}>
{/* ... */}
</NavigationContainer>
);
}
USAGE
Now you can import navigationRef in any of the file, even nested ones. And can use this to get the currentRoute and screen details.
//SomeNestedComonent.js
import { navigationRef } from "path/to/RootNavigation.js";
const route = navigationRef.current?.getCurrentRoute(); //current route object
const currentScreen = route.name; // current screen name
I have multiple TabNavigators nested in a BottomTabNavigator. I get the current route of the TabNavigator with:
const pathAndParams = props.navigation.router.getPathAndParamsForState(props.navigation.state) || {}
const activePath = pathAndParams.path
This worked for me (I did it inside my navigation drawer)!
const getCurrentRoute = nav => {
if (Array.isArray(nav.routes) && nav.routes.length > 0) {
return getCurrentRoute(nav.routes[nav.index]);
} else {
return nav.routeName;
}
};
const currentNavigation = getCurrentRoute(this.props.navigation.state);
If you are using reach navigation version 6 you can retrieve screen name by
props.route.name
We have a lot of answer here but it is hard to apply the fix because navigation is NULL.
WHY?
Scenario 1: We are using hooks function like: useRoute, useNavigationState,... but the navigation don't be mounted yet. So it is null and get the Error.
Scenario 2: We are using navigation object in the current screen like HomeScreen
const Home = ({ navigation, route }) => {
console.log("ROUTE", route);
// rest of your code
};
but navigation is NULL in Root app with presence of NavigationContainer
SOLUTION
Make sure to checking navigation is not NULL by using onReady() method of React navigation.
const navigationRef = useRef();
const [routeName, setRouteName] = useState('');
return (
<NavigationContainer
ref={navigationRef}
onReady={() => {
const currentRoute = navigationRef.current.getCurrentRoute();
setRouteName(currentRoute.name);
// Do whatever you want with navigation here!.
}}>
...
</NavigationContainer>);
That's it.
this worked for me try this..
const getCurrentRouteName = () => {
let _index = props.state.index;
let _routeName = props.state.routeNames;
return _routeName[_index]
}
For 'wix/react-native-navigation' below is my working solution,
import { Navigation } from 'react-native-navigation';
// Create a variable and set the value from navigation events
let navComponent = null
Navigation.events().registerComponentDidAppearListener(event => navComponent = event)
// navComponent will have the following structure
{"componentId": "Component9", "componentName": "HomeScreen", "componentType": "Component", "passProps": {}}
In my case, I needed to get the bottom nav index as well, this was my method
import {useNavigationState} from '#react-navigation/native';
then
const routes = useNavigationState(state => state.routes);
let place = routes[routes.length - 1];
if (place.name === 'YOUR_BOTTOM_NAV_NAME') {
if (place.state.index === 0) {
//you are in the main screen(BOTTOM_NAV : index 0)
} else {
//else navigate to index 0 screen
navigation.navigate('FirstScreen');
}
} else if (place.name === 'Another_Screen') {
navigation.navigate('navigate_to_the_place_you_want');
} else {
//otherwise come to the first screen
navigation.navigate('FirstScreen');
}
Try this,
const appNavigation = useNavigation();
const currentRoute = appNavigation.getCurrentRoute();
This worked for me. Navigation, and its state received as props were unreliable(at least for drawer navigator at root).
So I went with this one, which seems to be giving the global navigation state.
Had to use the navigation prop being received in drawer for drawer specific functions like closeDrawer or openDrawer.
export function AppDrawer(props) {
// for closeDrawer, openDrawer etc.
const { navigation: drawerNavigation } = props;
// for referencing current route
const appNavigation = useNavigation();
const currentRoute = appNavigation.getCurrentRoute();
// ... rest of the code
}
Reference for both the variable in console -
Hi, Thanks in advance, am using the Double click Component and it
works well for double click event. But I need to get an action when
user perform a single click. What the work around for this issue.
<DoubleClick onClick={(e) => this.hClick(value,e)}>
<View>
<Text>
{value.item}
</Text>
</View>
</DoubleClick>
I wrote a component.
// #flow
import * as React from 'react';
import { TouchableOpacity } from 'react-native';
import type { PressEvent } from 'react-native/Libraries/Types/CoreEventTypes';
type Props = {
children?: any,
onSingleTap: (event: PressEvent) => void,
onDoubleTap: (event: PressEvent) => void,
};
const MAX_DOUBLE_TOUCH_DISTANCE = 20;
const MAX_DOUBLE_TOUCH_DELAY_TIME = 250;
class SingleDoubleTap extends React.Component<Props> {
_timer: TimeoutID;
_previousPressEvent: ?PressEvent;
onPress = (event: PressEvent) => {
if (this._previousPressEvent) {
this.onReceiveSecondEvent(event);
} else {
this.onReceiveFirstEvent(event);
}
};
onReceiveFirstEvent = (event: PressEvent) => {
this._timer = setTimeout(() => {
this.props.onSingleTap(event);
this._previousPressEvent = null;
}, MAX_DOUBLE_TOUCH_DELAY_TIME);
this._previousPressEvent = event;
};
onReceiveSecondEvent = (event: PressEvent) => {
if (this._isDoubleTap(event)) {
this.props.onDoubleTap(event);
} else {
this.props.onSingleTap(event);
}
this._timer && clearTimeout(this._timer);
this._previousPressEvent = null;
};
_distanceBetweenTouches = (
touch1: PressEvent,
touch2: PressEvent
): number => {
const disX = touch1.nativeEvent.locationX - touch2.nativeEvent.locationX;
const disY = touch1.nativeEvent.locationY - touch2.nativeEvent.locationY;
return Math.sqrt(Math.pow(disX, 2) + Math.pow(disY, 2));
};
_isDoubleTap = (currentEvent: PressEvent) => {
if (!this._previousPressEvent) {
return false;
}
const distance = this._distanceBetweenTouches(
currentEvent,
this._previousPressEvent
);
// $FlowFixMe
const { nativeEvent } = this._previousPressEvent;
const delay = currentEvent.nativeEvent.timestamp - nativeEvent.timestamp;
return (
distance < MAX_DOUBLE_TOUCH_DISTANCE &&
delay < MAX_DOUBLE_TOUCH_DELAY_TIME
);
};
componentWillUnmount = () => {
this._timer && clearTimeout(this._timer);
};
render() {
return (
<TouchableOpacity onPress={this.onPress}>
{this.props.children}
</TouchableOpacity>
);
}
}
export default SingleDoubleTap;
How to use it?
<SingleDoubleTap
onSingleTap={this._onSingleTap}
onDoubleTap={this._onDoubleTap}>
..... // other components
</SingleDoubleTap>
The key thing is you should wait for the DoubleTap event failed to recognize the touch event as OneTap.
Remove the double click and use touchable component. pass the click to function and find whether its single or double click using timer delay
I am using SQLite as the device's database. What I am trying to basically achieve is this:
1- Give a user the ability to star his favorite "data"
2- Once the data gets saved in the db, retrieve it inside another page and insert them into a listView for the user to see at any time.
But no matter how much I try, I am always getting the same error.
Cannot read property of undefined.
The code:
import React, { Component } from 'react'
import {
View,
Text,
ListView
} from 'react-native'
var SQLite = require('react-native-sqlite-storage')
var db = SQLite.openDatabase({ name: "RHPC.db", location: "default"})
var obj;
class Schedules extends Component {
constructor(props) {
super(props)
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
});
this.state = {
datasource: []
}
db.transaction((tx) => {
tx.executeSql("SELECT * FROM schedules", [], (tx, res) => {
let len = res.rows.length;
if(len > 0) {
for(let i = 0; i < len; i++) {
var obj = [{id: res.rows.item(i)["id"], title: res.rows.item(i)["title"]}]
}
this.setState({
datasource: obj
})
} else {
console.log("empty")
}
})
}, (err) => {
console.log("error: " + JSON.stringify(err))
})
}
_renderRow(rowData) {
return(
<View>
<Text key={rowData.id}>
{rowData.title}
</Text>
</View>
)
}
render() {
console.log(this.state.datasource);
return(
<View style={{marginTop: 150}}>
<ListView
dataSource={this.state.datasource}
renderRow={this._renderRow.bind(this)}
/>
</View>
);
}
}
const styles = {
}
export default Schedules;
When I try to console.log the dataSource state:
0: Object
id: 2
title: "Session 1: Transition from Humanitarian Assistance to Rebuilding Health & Health Systems."
So in other words it looks like it's working but not 100%? Because I do have two rows inside that table and it's only retrieving the last one. Is this the cause of the undefined issue?
You use ListView in a wrong way, you create new dataSource in constructor (ds) and not assign it anywhere, checkout example in documentation: https://facebook.github.io/react-native/docs/listview.html
It should be:
constructor(props) {
super(props)
this.state = {
dataSource: new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}),
}
}
And in setState make something like this:
this.setState({
datasource: this.state.dataSource.cloneWithRows(obj)
})
Edit:
And in your for loop you should have:
var obj = [];
for(let i = 0; i < len; i++) {
obj.push({id: res.rows.item(i)["id"], title: res.rows.item(i)["title"]});
}