how can I prevent the app from minimizing / exiting when pushing back button on my device?
Im trying to assign "browsers back" functionality when pressing back button on my device, heres my code:
import 'react-native-get-random-values';
import React, { useState, useRef, Component, useEffect } from 'react'
import {
Alert,
SafeAreaView,
StyleSheet,
StatusBar,
View,
Text,
ScrollView,
BackHandler,
RefreshControl
} from 'react-native'
import WebView from 'react-native-webview'
import Icon from 'react-native-vector-icons/FontAwesome';
import { Button } from 'react-native-elements';
const App = () => {
function backButtonHandler(){}
function refreshHandler(){
if (webviewRef.current) webviewRef.current.reload()
}
useEffect(() => {
BackHandler.addEventListener("hardwareBackPress", backButtonHandler);
return () => {
BackHandler.removeEventListener("hardwareBackPress", backButtonHandler);
};
}, [backButtonHandler]);
let jscode = `window.onscroll=function(){window.ReactNativeWebView.postMessage(document.documentElement.scrollTop||document.body.scrollTop)}`;
const [canGoBack, setCanGoBack] = useState(false)
const [currentUrl, setCurrentUrl] = useState('')
const [refreshing, setRefreshing] = useState(false);
const [scrollviewState, setEnableRefresh] = useState(false);
const webviewRef = useRef(null)
const scrollviewRef = useRef(false)
backButtonHandler = () => {
if (webviewRef.current) webviewRef.current.goBack()
}
return (
<>
<StatusBar barStyle='dark-content' />
<SafeAreaView style={styles.flexContainer}>
<ScrollView
contentContainerStyle={styles.flexContainer}
refreshControl={
<RefreshControl refreshing={false} onRefresh={refreshHandler} ref={scrollviewRef} enabled={ (scrollviewState) ? true : false } />
}>
<WebView
source={{ uri: 'https://youtube.com' }}
startInLoadingState={true}
ref={webviewRef}
onNavigationStateChange={navState => {
setCanGoBack(navState.canGoBack)
setCurrentUrl(navState.url)
}}
injectedJavaScript={jscode}
onMessage={(event)=>{
let message = event.nativeEvent.data;
let num = parseInt(message);
if(num==0){setEnableRefresh(true)}
else{setEnableRefresh(false)}
}}
/>
</ScrollView>
<View style={styles.tabBarContainer}>
<Button onPress={backButtonHandler}
icon={
<Icon
name="arrow-left"
size={15}
color="white"
/>
}
containerStyle={styles.buttonx}
title="Back"
/>
</View>
</SafeAreaView>
</>
)
}
const styles = StyleSheet.create({
flexContainer: {
flex: 1
},
tabBarContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: 'orange'
},
buttonx: {
backgroundColor:'blue',
width:'100%'
}
})
export default App
my problem is when I press the back button on my device, it does call backButtonHandler function and my webview navigates back, but at the same time the app minimizes too.. is there a way to prevent this?
change your backButtonHandler Method to just return true, when your backHandler Method does return true, it actually does nothing onPress Back button :
backButtonHandler = () => {
return true;
}
Related
I can't set a state with Hooks in React Native. After call setMyvariable, I get an [object Object].
I can see that with a simple alert...
Please... Help me... Why append this...?
This is the code:
import * as React from 'react';
import { useState } from 'react';
import { View, TextInput, Button, StyleSheet } from 'react-native';
const PageArticle = () => {
const [FindRollNo, setFindRollNo] = useState("12");
const [RollNo, setRollNo] = useState(undefined);
const [StudentName, setStudentName] = useState(undefined);
const [Course, setCourse] = useState(undefined);
const SearchRecord = () => {
var FindRollNo = {FindRollNo};
alert(FindRollNo);
};
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }, styles.viewStyle}>
<TextInput
placeholder={'Enter RollNo'}
placeholderTextColor={'#ff0000'}
keyboardType={'numeric'}
style={styles.txtStyle}
onChangeText={(value) => setFindRollNo(value)}
/>
<Button title={'Find Record'} onPress={SearchRecord} />
<TextInput
style={styles.txtStyle}
value={RollNo}
/>
<TextInput style={styles.txtStyle} value={StudentName} />
<TextInput style={styles.txtStyle} value={Course} />
{/*<Text>Article Screen ciao </Text>*/}
</View>
);
};
You've wrapped FindRollNo in curly braces, which turns the value into an object.
Fortunately, you can access FindRollNo directly. Here's an Expo Snack showing how:
https://snack.expo.dev/#coolsoftwaretyler/trembling-blueberries
The code looks like this. Pay attention mostly to the difference in SearchRecord, which just alerts FindRollNo directly without creating a new variable or anything.
import * as React from 'react';
import { useState } from 'react';
import { View, TextInput, Button, StyleSheet } from 'react-native';
const PageArticle = () => {
const [FindRollNo, setFindRollNo] = useState("12");
const [RollNo, setRollNo] = useState(undefined);
const [StudentName, setStudentName] = useState(undefined);
const [Course, setCourse] = useState(undefined);
const SearchRecord = () => {
alert(FindRollNo);
};
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<TextInput
placeholder={'Enter RollNo'}
placeholderTextColor={'#ff0000'}
keyboardType={'numeric'}
onChangeText={(value) => setFindRollNo(value)}
/>
<Button title={'Find Record'} onPress={SearchRecord} />
<TextInput
value={RollNo}
/>
<TextInput value={StudentName} />
<TextInput value={Course} />
{/*<Text>Article Screen ciao </Text>*/}
</View>
);
};
export default PageArticle
Here's the function-
const setLoading = (value) => {
const messages = dashboards.data.message.filter((item) => {
const title = item.dashboardTitle || item.dashboardName;
return title.toLowerCase().startsWith(value.toLowerCase());
});
setFiltered(messages);
console.log(filtered);
};
I want to display the variable 'messages' separately in my app, how would I do that? 'messages' variable needs to be displayed within the default react native 'Text' component. I have written down 'messages' below within Text component but currently it's not displaying anything (since it is within function) -
import React, { useState, useEffect, useReducer } from 'react';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, Keyboard} from 'react-native';
import { Searchbar } from 'react-native-paper';
import { theme } from '../theme';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { apiStateReducer } from '../reducers/ApiStateReducer';
import CognitensorEndpoints from '../services/network/CognitensorEndpoints';
import DefaultView from '../components/default/DefaultView';
import DashboardListCard from '../components/DashboardListCard';
const AppHeader = ({
scene,
previous,
navigation,
searchIconVisible = false,
}) => {
const [dashboards, dispatchDashboards] = useReducer(apiStateReducer, {
data: [],
isLoading: true,
isError: false,
});
const [filtered, setFiltered] = useState([]);
const setLoading = (value) => {
const messages = dashboards.data.message.filter((item) => {
const title = item.dashboardTitle || item.dashboardName;
return title.toLowerCase().startsWith(value.toLowerCase());
});
setFiltered(messages);
console.log(filtered);
};
const dropShadowStyle = styles.dropShadow;
const toggleSearchVisibility = () => {
navigation.navigate('Search');
};
useEffect(() => {
CognitensorEndpoints.getDashboardList({
dispatchReducer: dispatchDashboards,
});
}, []);
return (
<>
<View style={styles.header}>
<View style={styles.headerLeftIcon}>
<TouchableOpacity onPress={navigation.pop}>
{previous ? (
<MaterialIcons
name="chevron-left"
size={24}
style={styles.visible}
/>
) : (
<MaterialIcons
name="chevron-left"
size={24}
style={styles.invisible}
/>
)}
</TouchableOpacity>
</View>
<Text style={styles.headerText}>
{messages}
</Text>
<View style={styles.headerRightIconContainer}>
{searchIconVisible ? (
<TouchableOpacity
style={[styles.headerRightIcon, dropShadowStyle]}
onPress={toggleSearchVisibility}>
<MaterialIcons name="search" size={24} style={styles.visible} />
</TouchableOpacity>
) : (
<View style={styles.invisible} />
)}
</View>
</View>
</>
);
};
If your messages variable is an array you can map it
{messages.map((message, key)=>(
<Text style={styles.headerText}>
{message.dashboardName}
</Text>
))}
Since your messages variable is stored in 'filtered' state, you can map it by doing this:
{filtered.map((item, index) => <Text key={index}>{item.dashboardName}<Text>)}
I am new to this so I hope this is the right place to get help!
As titled, executing this code is giving me the "Too many re-renders" error on React.
I have tried going through all lines and checking my hooks repeatedly, but nothing seems to work.
I am guessing this is happening due to useEffect, so pasting the code for the relevant components below:
UseResults:
import { useEffect, useState } from 'react';
import yelp from '../api/yelp';
export default () => {
const [results, setResults] = useState([]);
const [errorMessage, setErrorMessage] = useState('');
const searchApi = async () => {
try {
const response = await yelp.get('/search', {
params: {
limit: 50,
term,
location: 'san francisco'
}
});
setResults(response.data.businesses);
} catch (err) {
setErrorMessage('Something went wrong')
}
};
useEffect(() => {
searchApi('pasta');
}, []);
return [searchApi, results, errorMessage];
}
SearchScreen:
import React, { useState } from 'react';
import { Text, StyleSheet } from 'react-native';
import { ScrollView } from 'react-native-gesture-handler';
import ResultsList from '../components/ResultsList';
import SearchBar from '../components/SearchBar';
import useResults from '../hooks/useResults';
const SearchScreen = (navigation) => {
const [term, setTerm] = useState('');
const [searchApi, results, errorMessage] = useResults();
const filterResultsByPrice = (price) => {
return results.filter(result => {
return result.price === price;
});
};
return <>
<SearchBar
term={term}
onTermChange={setTerm}
onTermSubmit={searchApi()}
/>
{errorMessage ? <Text>{errorMessage}</Text> : null}
<Text>We have found {results.length} results</Text>
<ScrollView>
<ResultsList
results={filterResultsByPrice('$')}
title="Cost Effective"
navigation={navigation}
/>
<ResultsList
results={filterResultsByPrice('$$')}
title="Bit Pricier"
navigation={navigation}
/>
<ResultsList
results={filterResultsByPrice('$$$')}
title="Big Spender"
navigation={navigation}
/>
</ScrollView>
</>
};
const styles = StyleSheet.create({});
export default SearchScreen;
ResultsList:
import React from 'react';
import { View, Text, StyleSheet, FlatList } from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler';
import ResultsDetail from './ResultsDetail';
const ResultsList = ({ title, results, navigation }) => {
return (
<View style={styles.container} >
<Text style={styles.title}>{title}</Text>
<FlatList
horizontal
showsHorizontalScrollIndicator={false}
data={results}
keyExtractor={result => result.id}
renderItem={({ item }) => {
return (
<TouchableOpacity onPress={() => navigation.navigate('ResultsShow')}>
<ResultsDetail result={item} />
</TouchableOpacity>
)
}}
/>
</View>
);
};
const styles = StyleSheet.create({
title: {
fontSize: 18,
fontWeight: 'bold',
marginLeft: 15,
marginBottom: 5
},
container: {
marginBottom: 10
}
});
export default ResultsList;
TIA!
App has all old code which I am upgrading to latest versions. It was using Redux for state management with StackNavigator. Since that is not supported, I am not able to understand how to migrate my existing redux actions, which were changing screens on various events.
An example action:
export const goToHome = () => ({
type: PUSH,
routeName: 'projectList',
});
Which earlier reached navReducer, which handled POPing and PUSHing of screens.
export default (state = initialState, action) => {
let nextState;
switch (action.type) {
case NAV_POP:
nextState = AppNavigator.router.getStateForAction(
NavigationActions.goBack(),
state
);
break;
...
Please suggest.
Thanks.
The navigationRef is used for the scenarios like this.
You can refer the documentation here
It states
Sometimes you need to trigger a navigation action from places where
you do not have access to the navigation prop, such as a Redux
middleware. For such cases, you can dispatch navigation actions from
the navigation container
Which exactly is your requirement, here we create a navigationref and use call the navigation methods from there.
The below is the code for a simple example, you can use the 'navigate' inside your reducer. Also you will have to move it to a separate file just like they've provided in the documetation.
import * as React from 'react';
import { View, Button, Text } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
const navigationRef = React.createRef();
function navigate(name, params) {
navigationRef.current && navigationRef.current.navigate(name, params);
}
function Home() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title="Go to Settings"
onPress={() => navigate('Settings', { userName: 'Lucy' })}
/>
</View>
);
}
function Settings({ route }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Hello {route.params.userName}</Text>
<Button title="Go to Home" onPress={() => navigate('Home')} />
</View>
);
}
const RootStack = createStackNavigator();
export default function App() {
return (
<NavigationContainer ref={navigationRef}>
<RootStack.Navigator>
<RootStack.Screen name="Home" component={Home} />
<RootStack.Screen name="Settings" component={Settings} />
</RootStack.Navigator>
</NavigationContainer>
);
}
you can navigate through navigation container ref
you can call navigation().navigate("Settings") or navigation().goBack() in reducer
here is the demo of export navigation: https://snack.expo.io/#nomi9995/2eb7fd
App.js
import React,{useEffect} from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
const navRef = React.createRef();
export const navigation=()=>{
return navRef.current && navRef.current
}
const TestComponent=()=> {
useEffect(()=>{
setTimeout(() => {
navigation().navigate("Settings")
setTimeout(() => {
navigation().goBack()
}, 3000);
}, 100);
})
return (
<View style={styles.container}>
<Text>TestComponent 1</Text>
</View>
);
}
const TestComponent2=()=> {
return (
<View style={styles.container}>
<Text>TestComponent 2</Text>
</View>
);
}
const RootStack = createStackNavigator();
export default function App() {
return (
<NavigationContainer ref={navRef}>
<RootStack.Navigator>
<RootStack.Screen name="Home" component={TestComponent} />
<RootStack.Screen name="Settings" component={TestComponent2} />
</RootStack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
reducder.js
import { navigation } from 'path of App.js';
export default (state = initialState, action) => {
let nextState;
switch (action.type) {
case NAV_POP:
nextState = navigation().goBack()
break;
The intended function is that it would just delete the one goal that is clicked but for some odd reason it decides onPress to delete all goals listed.
I am following this tutorial https://www.youtube.com/watch?v=qSRrxpdMpVc and im stuck around 2:44:45. If anyone else has done this tutorial and or can see my problem an explanation would be greatly appreciated. :)
Program
import React, { useState } from "react";
import {
StyleSheet,
Text,
View,
Button,
TextInput,
ScrollView,
FlatList
} from "react-native";
import GoalItem from "./components/GoalItem";
import GoalInput from "./components/GoalInput";
export default function App() {
const [courseGoals, setCourseGoals] = useState([]);
const addGoalHandler = goalTitle => {
setCourseGoals(currentGoals => [
...currentGoals,
{ key: Math.random().toString(), value: goalTitle }
]);
};
const removeGoalHander = goalId => {
setCourseGoals(currentGoals => {
return currentGoals.filter((goal) => goal.id !== goalId);
});
};
return (
<View style={styles.screen}>
<GoalInput onAddGoal={addGoalHandler} />
<FlatList
keyExtractor={(item, index) => item.id}
data={courseGoals}
renderItem={itemData => (
<GoalItem
id={itemData.item.id}
onDelete={removeGoalHander}
title={itemData.item.value}
/>
)}
></FlatList>
</View>
);
}
const styles = StyleSheet.create({
screen: {
padding: 80
}
});
Function
import React from "react";
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
const GoalItem = props => {
return (
<TouchableOpacity onPress={props.onDelete.bind(this, props.id)}>
<View style={styles.listItem}>
<Text>{props.title}</Text>
</View>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
listItem: {
padding: 10,
backgroundColor: "lightgrey",
borderColor: "grey",
borderRadius: 5,
borderWidth: 1,
marginVertical: 10
}
});
export default GoalItem;
When updating state, you have to pass new array so component can detect changes and update view
const removeGoalHander = goalId => {
setCourseGoals(currentGoals => {
const newGoals = currentGoals.filter((goal) => goal.id !== goalId);
return [...newGoals];
});
};