I am currently trying to learn React Native, but I already struggle in the Networking Part of the Tutorial.
This is the code:
import React, { Component } from 'react';
import { AppRegistry, Text, TextInput, View } from 'react-native';
class App extends Component {
function getMoviesFromApiAsync() {
return fetch('https://facebook.github.io/react-native/movies.json')
.then((response) => response.json())
.then((responseJson) => {
return responseJson.movies;
})
.catch((error) => {
console.error(error);
});
}
render() {
getMoviesFromApiAsync();
};
}
AppRegistry.registerComponent('testproject', () => App);
And I get the following error:
In my case Line 5, Char 10 would be: function so it expects something else after funct.
Here is an example of using that function:
import React, { Component } from 'react';
import { AppRegistry, Text, TextInput, View } from 'react-native';
class App extends Component {
constructor(props) {
super(props);
this.state = { movies: [] }
}
componentDidMount() {
this.getMoviesFromApiAsync();
}
getMoviesFromApiAsync() {
return fetch('https://facebook.github.io/react-native/movies.json')
.then((response) => response.json())
.then((responseJson) => {
this.setState({ movies: responseJson.movies });
})
.catch((error) => {
console.error(error);
});
}
renderMovies = () =>
this.state.movies.map((movie, i) => <Text key={i}>{movie.title}</Text>)
render() {
return (
<View style={{flex: 1}}>
{this.renderMovies()}
</View>
)
};
}
AppRegistry.registerComponent('testproject', () => App);
import React, { Component } from 'react';
import { AppRegistry, Text, TextInput, View } from 'react-native';
class App extends Component {
state = {
movies: null
}
componentDidMount() {
const movies = this.getMoviesFromApiAsync();
this.setState({movies: movies});
}
getMoviesFromApiAsync() {
return fetch('https://facebook.github.io/react-native/movies.json')
.then((response) => response.json())
.then((responseJson) => {
return responseJson.movies;
})
.catch((error) => {
console.error(error);
});
}
render() {
const { movies } = this.state;
if (!movies) return null;
return (
<View>
{
movies.map((movie, index) => {
console.log("movie:", movie);
return(
<View key={index}>
<Text>{movie.name}</Text>
</View>
)
})
}
</View>
)
};
}
AppRegistry.registerComponent('testproject', () => App);
1 - ) So first set variable in state movies null cause u dont have any movies data
2 - ) Read React Component Lifecycle ComponentDidMount run after render and call getMovies func for fetch data and write in the state with this.setState
3 - ) Check u have movies with if(!movies) return null; or return ActivityIndicator for loading but if u dont get movies activity indicator run forever.
4 - ) this.setState render your component again with new state
Related
I wish to utilize an in-app notification system, aka a more attractive and less in your face' use of alerts to let the user know what actions are being done, especially when for instance a barcode has been detected but it needs to send that barcode to the server and the user needs to wait.
I have found this lib and have attempted to implement it; but as I am using React Navigation and I wish to render the item at the very top of the application, it gets cut off by React Native header
Is it possible to have a function I can create and reference whenever I want a global notification and it will render on the very top I would imagine it would need to render here:
import React from 'react';
import { createBottomTabNavigator,createStackNavigator } from 'react-navigation';
import SearchTab from './components/Tabs/SearchTab';
import HomeTab from './components/Tabs/HomeTab';
import ScannerTab from './components/Tabs/ScannerTab';
import SettingsTab from './components/Tabs/SettingsTab';
import Ionicons from 'react-native-vector-icons/Ionicons';
import StockModal from './components/Modals/StockModal';
const MainStack = createBottomTabNavigator(
{
Home: HomeTab,
Search: SearchTab,
Scanner: ScannerTab,
Settings: SettingsTab,
//Todo: Total overlay modals HERE
},
{
navigationOptions: ({ navigation }) => ({
tabBarIcon: ({ focused, tintColor }) => {
const { routeName } = navigation.state;
let iconName;
if (routeName === 'Home') {
iconName = `ios-information-circle${focused ? '' : '-outline'}`;
} else if (routeName === 'Settings') {
iconName = `ios-options${focused ? '' : '-outline'}`;
}else if (routeName === 'Scanner') {
iconName = `ios-barcode${focused ? '' : '-outline'}`;
}else if (routeName === 'Search') {
iconName = `ios-search${focused ? '' : '-outline'}`;
}
return <Ionicons name={iconName} size={25} color={tintColor} />;
},
}),
tabBarOptions: {
activeTintColor: 'tomato',
inactiveTintColor: 'gray',
},
}
);
export default RootStack = createStackNavigator(
{
Main: {
screen: MainStack,
},
QuickStockScreen: {
screen: StockModal,
},
},
{
mode: 'modal',
headerMode: 'none',
}
);
But even if that's possible, I am not sure how its possible to build a function that tells the notification to show; React Redux comes to mind but I don't wish to implement such a cumbersome system just for one feature and it was something I considered when creating his application and decided against.
The notification system in question (not very clear documentation or examples sadly) https://www.npmjs.com/package/react-native-in-app-notification
Here is the navigation lib I am using: https://reactnavigation.org/
What you want would be a component that is a the same level of the navigation (So it can display over it). In multiple projects, I use react-native-root-siblings to do so. It allows you to add UI over the app and so over the navigation.
An exemple how what I made with it. The dark layer and the box at the bottom are part of the Siblings Component.
https://gyazo.com/7ad3fc3fea767ea84243aaa493294670
The Siblings is used like the Alert of React-Native, so as a function (which is quite useful!)
messageMenu.js
import React, { Component } from 'react';
import RootSiblings from 'react-native-root-siblings';
import MessageMenuContainer from './MessageMenuContainer';
export default class Dialog extends Component {
static show = (props) => new RootSiblings(<MessageMenuContainer {...props} />);
static update = (menu, props) => {
if (menu instanceof RootSiblings) {
menu.update(<MessageMenuContainer {...props} />);
} else {
console.warn(`Dialog.update expected a \`RootSiblings\` instance as argument.\nBut got \`${typeof menu}\` instead.`);
}
}
static close = (menu) => {
if (menu instanceof RootSiblings) {
menu.destroy();
} else {
console.warn(`Dialog.destroy expected a \`RootSiblings\` instance as argument.\nBut got \`${typeof menu}\` instead.`);
}
}
render() {
return null;
}
}
export {
RootSiblings as Manager,
};
Where the MessageMenuContainer is your component to render at the top.
Component using the Root Siblings:
import React from 'react';
import PropTypes from 'prop-types';
import I18n from 'react-native-i18n';
import { BackHandler, Keyboard, Platform, TouchableOpacity } from 'react-native';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import DraftMenu from './messageMenu'; //HERE IS THE IMPORT YOU WANT
import { Metrics, Colors, Fonts } from '../../main/themes';
class DraftBackButton extends React.Component {
state = {
draftMenu: undefined,
}
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackAndroid);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackAndroid);
}
handleBackAndroid = () => {
this.handleBack();
return true;
}
handleBack = async () => {
Keyboard.dismiss();
await this.openDraftMenu();
}
openDraftMenu = async () => {
if (this.state.draftMenu) {
await DraftMenu.update(this.state.draftMenu, this.draftMenuProps());
} else {
const draftMenu = await DraftMenu.show(this.draftMenuProps());
this.setState({ draftMenu: draftMenu });
}
}
draftMenuProps = () => ({
options: [
{ title: I18n.t('message.deleteDraft'), onPress: this.deleteDraft, icon: 'trash' },
{ title: I18n.t('message.saveDraft'), onPress: this.saveOrUpdateDraft, icon: 'documents' },
{ title: I18n.t('cancel'), icon: 'close', style: { backgroundColor: Colors.tertiaryBackground } },
],
destroyMenuComponent: async () => {
DraftMenu.close(this.state.draftMenu);
await this.setState({ draftMenu: undefined });
},
withIcon: true,
})
saveOrUpdateDraft = async () => {
// SAVE OR UPDATE DRAFT. NOT IMPORTANT
}
saveDraft = async () => {
// SAVING THE DRAFT
}
updateDraft = async () => {
// UPDATING THE DRAFT
}
deleteDraft = async () => {
// DELETING THE DRAFT
}
render() {
return (
<TouchableOpacity
hitSlop={Metrics.touchable.largeHitSlop}
onPress={() => {
this.handleBack();
}}
>
<Text>BUTTON</Text>
</TouchableOpacity>
);
}
}
DraftBackButton.propTypes = {
// ALL THE PROPTYPES
};
function mapStateToProps(state, ownProps) {
//
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({ fetchMessages }, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(DraftBackButton);
The best thing with this lib is that you can call the .show anywhere in your app and it will render at the very top!
Hope it's what you're looking for!
EDIT:
I updated the example of how to use the Root Siblings.
Here's the content of my MessageContainer which will be display on top of everything
import React from 'react';
import PropTypes from 'prop-types';
import { Animated, Dimensions, InteractionManager, StyleSheet, TouchableOpacity, View } from 'react-native';
import MessageMenuItem from './MessageMenuItem';
import { Colors } from '../../../main/themes';
const { width, height } = Dimensions.get('window');
const OPTION_HEIGHT = 55;
const OVERLAY_OPACITY = 0.5;
export default class DraftMenuContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
animatedHeight: new Animated.Value(0),
animatedOpacity: new Animated.Value(0),
menuHeight: props.options.length * OPTION_HEIGHT,
};
}
componentDidMount() {
this.onOpen();
}
// Using Animated from react-native to make the animation (fade in/out of the dark layer and the dimensions of the actual content)
onOpen = async () => {
await this.state.animatedHeight.setValue(0);
await this.state.animatedOpacity.setValue(0);
Animated.parallel([
Animated.timing(this.state.animatedHeight, { toValue: this.state.menuHeight, duration: 200 }),
Animated.timing(this.state.animatedOpacity, { toValue: OVERLAY_OPACITY, duration: 200 }),
]).start();
}
onClose = async () => {
await this.state.animatedHeight.setValue(this.state.menuHeight);
await this.state.animatedOpacity.setValue(OVERLAY_OPACITY);
Animated.parallel([
Animated.timing(this.state.animatedHeight, { toValue: 0, duration: 200 }),
Animated.timing(this.state.animatedOpacity, { toValue: 0, duration: 200 }),
]).start(() => this.props.destroyMenuComponent()); // HERE IS IMPORTANT. Once you're done with the component, you need to destroy it. To do so, you need to set a props 'destroyMenuComponent' which is set at the creation of the initial view. See the other code what it actually do
}
render() {
return (
<View style={styles.menu}>
<Animated.View style={[styles.backgroundOverlay, { opacity: this.state.animatedOpacity }]}>
<TouchableOpacity
activeOpacity={1}
onPress={() => this.onClose()}
style={{ flex: 1 }}
/>
</Animated.View>
<Animated.View style={[styles.container, { height: this.state.animatedHeight }]}>
{this.props.options.map((option, index) => (
<MessageMenuItem
height={OPTION_HEIGHT}
icon={option.icon}
key={index}
onPress={async () => {
await this.onClose();
InteractionManager.runAfterInteractions(() => {
if (option.onPress) {
option.onPress();
}
});
}}
style={option.style}
title={option.title}
withIcon={this.props.withIcon}
/>
))}
</Animated.View>
</View>
);
}
}
DraftMenuContainer.propTypes = {
destroyMenuComponent: PropTypes.func.isRequired,
withIcon: PropTypes.bool,
options: PropTypes.arrayOf(PropTypes.shape({
icon: PropTypes.string.isRequired,
onPress: PropTypes.func,
title: PropTypes.string.isRequired,
})),
};
I'd like to use service account authentication within my react native app, in order to let people update a spreadsheet without the need for them to log into their google account.
I'm pretty lost on how to do it, since all frontend code I see is related to traditional oauth2 login.
Any idea?
I finally dropped the idea of service account and used client oauth.
I relied on the following React Native library : https://github.com/joonhocho/react-native-google-sign-in
Here is my authentication service (simplified, redux + redux thunk based):
// <project_folder>/src/services/auth.js
import GoogleSignIn from 'react-native-google-sign-in';
import { store } from '../store';
import auth from '../actions/auth';
export default () => {
GoogleSignIn.configure({
// https://developers.google.com/identity/protocols/googlescopes
scopes: ['https://www.googleapis.com/auth/spreadsheets'],
clientID: 'XXXXXXXXXXXXXXXXX.apps.googleusercontent.com',
}).then(() => {
GoogleSignIn.signInPromise().then((user) => {
store.dispatch(auth(user.accessToken));
}).catch((err) => {
console.log(err);
});
}).catch((err) => {
console.log(err);
});
};
So then I have the user.accessToken in my redux store and can use it elsewhere to create REST requests to the google sheets API.
Here is a simple example of a component that handles the auth and then retrieves some data from the sheet:
// <project_folder>/src/main.js
import React, { Component } from 'react';
import {
ActivityIndicator,
Text,
View,
StyleSheet,
} from 'react-native';
import { connect } from 'react-redux';
import auth from './services/auth';
import Sheet from './services/sheet';
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
const sheetId = 'XX-XXXXXX_XXX_XXXXXXXXXXXXXXXXXXXXXX';
class Main extends Component {
constructor(props) {
super(props);
this.state = {
animating: true,
};
}
componentDidMount() {
auth();
}
componentWillUpdate(nextProps) {
if (this.props.token !== nextProps.token) this.setState({ animating: false });
}
componentDidUpdate(nextProps) {
this.sheet = new Sheet(id, this.props.token);
this.sheet.getDoc();
}
render() {
return (
<View style={styles.container}>
<ActivityIndicator
animating={this.state.animating}
style={{ height: 80 }}
size="large"
/>
{!this.state.animating &&
<Text>
{this.props.name}
</Text>
}
</View>
);
}
}
const mapStateToProps = (state) => {
return {
token: state.auth.get('token'),
name: state.sheet.get('name'),
};
};
export default connect(mapStateToProps)(Main);
And here is a basic service to read/write a sheet:
// <project_folder>/services/sheet.js
import { store } from '../store';
import {
nameGet,
valueGet,
valuePost,
}
from '../actions/sheet';
class Sheet {
constructor(id, token) {
this.id = id;
this.token = token;
this.endPoint = `https://sheets.googleapis.com/v4/spreadsheets/${this.id}`;
}
getDoc() {
fetch(`${this.endPoint}?access_token=${this.token}`).then((response) => {
response.json().then((data) => {
console.log(data);
store.dispatch(nameGet(data.properties.title));
});
});
}
getCell(sheet, cell) {
const range = `${sheet}!${cell}`;
fetch(`${this.endPoint}/values/${range}?access_token=${this.token}`)
.then((response) => {
response.json()
.then((data) => {
console.log(data);
store.dispatch(valueGet(data.values[0][0]));
})
.catch((err) => {
console.log(err);
});
})
.catch((err) => {
console.log(err);
});
}
writeCell(sheet, cell, value) {
const range = `${sheet}!${cell}`;
const body = JSON.stringify({ values: [[value]] });
fetch(
`${this.endPoint}/values/${range}?valueInputOption=USER_ENTERED&access_token=${this.token}`,
{
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body,
}
)
.then((response) => {
response.json()
.then((data) => {
console.log(data);
store.dispatch(valuePost('OK'));
})
.catch((err) => {
console.log(err);
});
})
.catch((err) => {
console.log(err);
});
}
}
export default Sheet;
If there is a better way to do, please let me know.
I have no fully idea about this but yes if you can set the sheet permission as public and access from your application it will not ask you authentication.
also you may use below link may help more technical Google Sheets API v4
I can't display json data in listview. I get json data in console.log but not in listview isLoading is always on false.
I dont get any errors .catch(error => console.warn("error")).
Result on screen is first View because this.state.isLoading is false.
Here is a code:
import React, { Component } from 'react';
import { AppRegistry, StyleSheet, ListView, Text, View,Image,TouchableHighlight } from 'react-native';
var productArray = [];
class ListViewDemo extends Component {
constructor(props) {
console.warn("constructor");
super(props);
var dataSource = new ListView.DataSource({rowHasChanged:(r1,r2) => r1.guid != r2.guid});
this.state = {
dataSource: dataSource.cloneWithRows(productArray),
isLoading:true
}
}
componentDidMount() {
console.warn("componentDidMount");
this.getTheData(function(json){
productArray = json;
console.warn(productArray);
this.setState = ({
datasource:this.state.dataSource.cloneWithRows(productArray),
isLoading:false
})
}.bind(this));
console.warn("component -> " + this.state.isLoading);
}
getTheData(callback) {
console.warn("callback");
var url = "https://raw.githubusercontent.com/darkarmyIN/React-Native-DynamicListView/master/appledata.json";
fetch(url)
.then(response => response.json())
.then(json => callback(json))
.catch(error => console.warn("error"));
}
renderRow(rowData, sectionID, rowID) {
console.warn("renderRow");
return (
<TouchableHighlight underlayColor='#dddddd' style={{height:44}}>
<View>
<Text style={{fontSize: 20, color: '#000000'}} numberOfLines={1}>{rowData.display_string}</Text>
<Text style={{fontSize: 20, color: '#000000'}} numberOfLines={1}>test</Text>
<View style={{height: 1, backgroundColor: '#dddddd'}}/>
</View>
</TouchableHighlight>
);
}
render() {
console.warn("render" + this.state.isLoading);
var currentView = (this.state.isLoading) ? <View style={{height: 110, backgroundColor: '#dddddd'}} /> : <ListView dataSource={this.state.dataSource} renderRow={this.renderRow.bind(this)} enableEmptySections={true}/>
return(
<View>
{currentView}
</View>
);
}
}
// App registration and rendering
AppRegistry.registerComponent('AwesomeProject', () => ListViewDemo);
I see a couple of mistakes here.
In your componentDidMount, you are setting datasource intead of dataSource:
componentDidMount() {
console.warn("componentDidMount");
this.getTheData(function(json){
productArray = json;
console.warn(productArray);
this.setState = ({
//datasource:this.state.dataSource.cloneWithRows(productArray),
dataSource:this.state.dataSource.cloneWithRows(productArray),
isLoading:false
})
}.bind(this));
console.warn("component -> " + this.state.isLoading);
}
That's why you're not being able to render, because dataSource is never populated. It is just a little spelling mistake.
You are probably not getting into the second then in your getTheData method because you are not returning a Promise:
getTheData(callback) {
console.warn("callback");
var url = "https://raw.githubusercontent.com/darkarmyIN/React-Native-DynamicListView/master/appledata.json";
fetch(url)
//.then(response => response.json())
.then(response => return response.json())
.then(json => callback(json))
.catch(error => console.warn("error"));
}
Your are making a mistake with your setState, your are assigning it instead of calling it:
//this.setState = ({
// datasource:this.state.dataSource.cloneWithRows(productArray),
// isLoading:false
//})
this.setState({
dataSource:this.state.dataSource.cloneWithRows(productArray),
isLoading:false
})
Let me know if it works.
You are setting the function setState instead of calling it
this.setState = ({
datasource:this.state.dataSource.cloneWithRows(productArray),
isLoading:false
})
should be
this.setState({
datasource:this.state.dataSource.cloneWithRows(productArray),
isLoading:false
})
I want to prevent the user from going back to the previous screen. So I added code, but this does not work. Are there any solutions for this? The alert pop up is seen but "return false" does not work.
componentDidMount() {
BackAndroid.addEventListener('hardwareBackPress', () => {
Alert.alert("alert","alert")
this.props.navigator.pop();
return false;
});
You need to return true, if you want to disable the default back button behavior.
Here is a sample component which will block the user from going back to previous screen.
import React, {Component,} from 'react';
import {
View,
Text,
BackHandler,
ToastAndroid,
} from 'react-native';
class BackButtonDemo extends Component {
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
}
handleBackButton() {
ToastAndroid.show('Back button is pressed', ToastAndroid.SHORT);
return true;
}
render() {
return (
<View>
<Text>Back button example</Text>
</View>
);
}
}
module.exports = BackButtonDemo;
Note:
Also remove this.props.navigator.pop(); from your solution.
Navigator pop function will take the user to the previous screen rendered by Navigator.
Shiny new implementation using react hooks:
import React, {Component, useEffect} from 'react';
import {
View,
Text,
BackHandler,
} from 'react-native';
const LogInView = () => {
useEffect(() => {
const backHandler = BackHandler.addEventListener('hardwareBackPress', () => true)
return () => backHandler.remove()
}, [])
return (
<View>
<Text>Back button example</Text>
</View>
);
}
export default LoginView
I disable my back button (android) for whole application by add this code in App.js
componentDidMount() {
BackAndroid.addEventListener('hardwareBackPress', this.handleBackButton);
}
componentWillUnmount() {
BackAndroid.removeEventListener('hardwareBackPress', this.handleBackButton);
}
handleBackButton() {
return true;
}
don't forget to import BackAndroid
import {BackAndroid} from 'react-native'
Try this Disable back button by just returning true
import {BackAndroid} from 'react-native';
componentWillMount() {
BackAndroid.addEventListener('hardwareBackPress', () => {return true});
}
So While Everyone is working with the Backhandler of react-native I tried to do it with react-navigation to prevent going back handler.
This worked for me.
If you just want to prevent going back without showing or alerting anything to the user.
React.useEffect(() => {
navigation.addListener('beforeRemove', (e) => {
e.preventDefault();
});
}, [navigation]);
You can put this code in your function component.
Use Case:
In User Registration when the user signup and go to the confirmation
screen for the code so we dont want him back you can use this code at
the moment.
Just to give you a complete answer when using react-navigation:
If you're using react-navigation, place the following in your RootNavigation class not the App.js in order to disable the back-button for the whole application.
import { BackHandler } from 'react-native';
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.onBackButtonPressed);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.onBackButtonPressed);
}
onBackButtonPressed() {
return true;
}
using the BackHandler from react native worked for me. Just include this line in your ComponentWillMount:
BackHandler.addEventListener('hardwareBackPress', function() {return true})
it will disable back button on android device.
If you are using react-natigation then you need to use BackHandler instead of BackAndroid
import { BackHandler } from 'react-native';
// code
componentDidMount() {
BackHandler.addEventListener('backPress');
}
// some more code
componentWillUnmount() {
BackHandler.removeEventListener('backPress');
}
Disable back button for module react-navigation, use hook useFocusEffect
const hardwareBackPressCustom = useCallback(() => {
return true;
}, []);
useFocusEffect(() => {
BackHandler.addEventListener('hardwareBackPress', hardwareBackPressCustom)
return () => {
BackHandler.removeEventListener('hardwareBackPress', hardwareBackPressCustom)
};
}, []);
For me, this was the solution:
import React, { useEffect } from 'react'
import { View } from 'react-native'
function Home({ navigation }) {
useEffect(() =>
navigation.addListener('beforeRemove', (e) => {
e.preventDefault();
return
}),
[navigation]
);
return (
<View>
...
</View>
)
}
export default Home
You can see an example in docs
The only correct solution can be found here: https://reactnavigation.org/docs/custom-android-back-button-handling/
function ScreenWithCustomBackBehavior() {
// ...
useFocusEffect(
React.useCallback(() => {
const onBackPress = () => {
return true;
};
BackHandler.addEventListener('hardwareBackPress', onBackPress);
return () =>
BackHandler.removeEventListener('hardwareBackPress', onBackPress);
}, [isSelectionModeEnabled, disableSelectionMode])
);
// ...
}
All the others are for class components or apply to all screens, rather than only the screen where the hook is applied.
Just an update for Version 6.x as seen in RN docs
function ScreenWithCustomBackBehavior() {
// ...
useFocusEffect(
React.useCallback(() => {
const onBackPress = () => {
if (isSelectionModeEnabled()) {
disableSelectionMode();
return true;
} else {
return false;
}
};
BackHandler.addEventListener('hardwareBackPress', onBackPress);
return () =>
BackHandler.removeEventListener('hardwareBackPress', onBackPress);
}, [isSelectionModeEnabled, disableSelectionMode])
);
// ...
}
Alternatively as a hook
import {useNavigation} from '#react-navigation/native';
import {useEffect, useState, useCallback} from 'react';
export const usePreventGoBack = () => {
const navigation = useNavigation();
const [allow, setAllow] = useState(false);
const beforeRemoveListener = useCallback(
e => {
if (allow) {
return;
}
e.preventDefault();
},
[allow],
);
useEffect(() => {
const unsub = navigation.addListener('beforeRemove', beforeRemoveListener);
return () => {
unsub();
};
}, [navigation, beforeRemoveListener, allow]);
return (cb: () => void) => {
setAllow(true);
setTimeout(() => {
cb();
});
};
};
To bypass
const continuePressed = () => {
allowBackButton(() => {
navigation.popToTop();
});
};
import React, {useEffect} from 'react';
import {BackHandler} from 'react-native';
useEffect(() => {
const backHandler = BackHandler.addEventListener(
'hardwareBackPress',
() => {
return true;
},
);
return () => backHandler.remove();
})
For IOS use this code... In the Stack Navigation. react-navigation > 5
<LoginStackNav.Screen name="Home" component={Home} options={{gestureEnabled: false }}/>
Onclick of hardware back button OnBackPressed callback gets called
you can just remove super declaration in onBackPressed call back.
#Override
public void onBackPressed() {
}
I have this piece of react-native code:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
ToolbarAndroid,
ListView,
Text,
View
} from 'react-native';
let styles = require('./styles/styles');
class Sunshine extends Component {
constructor(props) {
super(props);
this.state = {isLoading: true, jsonData: ''}
}
componentDidMount() {
this.setState({jsonData: this.getMoviesFromApiAsync()})
}
render() {
if(this.state.isLoading != true) {
return (
<View style={styles.container}>
<ToolbarAndroid
style={styles.baseToolbar}
logo={require('./ic_launcher.png')}
title="Sunshine"
titleTextColor="red"/>
<View style={styles.viewcontainer}>
<Text>{this.state.jsonData.city.id}</Text>
<ListView
dataSource={this.state.jsonData.list}
renderRow={(rowData) => <Text>{rowData.dt}</Text>}
/>
</View>
</View>
);
} else {
return (
<View style={styles.container}>
<ToolbarAndroid
style={styles.baseToolbar}
logo={require('./ic_launcher.png')}
title="Sunshine"
titleTextColor="red"/>
<View style={styles.singleviewcontainer}>
<Text>Loading...</Text>
</View>
</View>
);
}
}
getMoviesFromApiAsync() {
return fetch('http://api.openweathermap.org/data/2.5/forecast/daily?q=94043&mode=json&units=metric&cnt=14&APPID=18dcba27e5bca83fe4ec6b8fbeed7827')
.then((response) => response.json())
.then((responseJson) => {
this.setState({isLoading: false, jsonData: responseJson});
console.log(responseJson);
return responseJson;
})
.catch((error) => {
console.error(error);
});
}
}
AppRegistry.registerComponent('Sunshine', () => Sunshine);
What I think it should happen is that when an answer arrives from the server, the list is populated with it's result. But that's not what's going on. Intsead i get this error:
undefined is not an object (evaluating 'allRowIDs.length')
So what exactly am i doing wrong here?
You have to create a ListViewDataSource with the data list.
constructor (props) {
super(props)
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
})
}
componentDidMount () {
// You don't need to assign the return value to the state
this.getMoviesFromApiAsync()
}
render () {
// Use the dataSource
const rows = this.dataSource.cloneWithRows(this.state.jsonData.list || [])
...
return (
...
<ListView
dataSource={rows}
/>
)
}
Full docs here.