React Native Modal crashes app on load - react-native

I'm trying to create a small request box that pops up from the bottom of the screen to let a user confirm a reservation. A mockup of what that could look like is below. After some research, a modal seemed like the way to go.
The Problem
Starting the app with a <Modal /> freezes the app on the home screen. Loading the modal when needed (in the code below) crashes the app with no error.
Code
Component rendering the <Model /> (called as <RequestTeacherPopup />)
// #flow
import React from 'react'
import { ScrollView, View } from 'react-native'
...
export default class RequestScreen extends React.Component<Props, {nextSeminar: DateTime, teachers: []}> {
constructor (props: Props) {
super(props)
...
this.state = {
nextSeminar: nextSeminar,
teachers: null,
requestVisibility: false,
requestedTeacher: null
}
}
...
render () {
var teacherList = []
if (this.state.teachers) {
for (let teacherItem of this.state.teachers) {
if (teacherItem.key !== this.props.profile.defaultSeminar) {
let teacher: Teacher = teacherItem.value
let teacherPic: {uri: string} = ('picture' in teacher) ? { uri: teacher.picture } : null
teacherList.push(
<ListItem
roundAvatar
avatar={teacherPic}
onPressRightIcon={
function () {
this.setState({
requestVisibility: true,
requestedTeacher: teacher
})
}.bind(this)
}
key={teacherItem.key}
title={`${teacher.firstName} ${teacher.lastName}`}
subtitle={`${teacher.taughtCourses} | Room ${teacher.room}`} />
)
}
}
}
return (
<View style={styles.mainContainer}>
<ScrollView>
<List>
{teacherList}
</List>
{
(this.state.requestVisibility)
? (<RequestTeacherPopup
requestedTeacher={this.state.requestedTeacher}
onFinish={() => this.setState({ requestVisibility: false })} />)
: null
}
</ScrollView>
</View>
)
}
}
RequestTeacherPopup Component
// #flow
import React, { Component } from 'react'
import { View, Image, Text } from 'react-native'
import { Divider, Overlay } from 'react-native-elements'
import Modal from 'react-native-modal'
...
class RequestTeacherPopup extends Component<{isVisible: boolean, requestedTeacher: Teacher, onFinish: () => void}> {
state = {
markedDates: this.getDaysInMonth(DateTime.local().month, DateTime.local().year, DISABLED_DAYS),
calVisiblity: false,
requestedDate: null,
requestedDay: null
}
...
render () {
return (
<Modal
style={Styles.bottomModal}
isVisible
onSwipe={this.handleRequest}
swipeDirection='up' >
...
</Modal>
)
}
}
The problem still occurs when the <Modal /> is not passed any children, and I have tried both the built in modal and the react-native-modal package from npm.
Thanks for any help!

Hello while showing or closing any modal,
you have use setTimeout(()=>{},miliseconds)
modal is an external window of the screen that covers the whole screen.

Related

React call function and setstate when go back to a previous screen

I'm new in React's world
I have 2 screens : Stock and Barcode.
In Stock, i navigate to Barcode's screen.
When i scan a barcode, i go back to the previous screen I would like to set the input text with the barcode and call a function. In my example joinData();
The problem is to set the input text and call a function.
I tried examples and answers but i don't find or don't understand how to to that.
I tried something in componentDidUpdate() but it fails
Invariant Violation:Maximum update depth exceeded
Stock.js
import React, {useState} from "react";
import { ScrollView, TouchableWithoutFeedback, Dimensions, StyleSheet, FlatList, View, Alert, TouchableOpacity, TextInput } from 'react-native';
//galio
import { Block, Text, theme } from "galio-framework";
import { Button, Icon, Input } from "../components/";
export default class Stock extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
this.array = [];
this.state = {
arrayHolder: [],
Input_ITMREF: ''
};
}
// I tried this but it fails
componentDidUpdate() {
if (this.props.navigation.getParam('itmref') != 'undefined') {
this.setState({ Input_ITMREF: this.props.navigation.getParam('itmref')});
}
}
componentDidMount() {
this.setState({ arrayHolder: [...this.array] }) // RafraƮchit la liste
}
joinData = () => {
vxml = this.state.Input_ITMREF+" I do something";
}
Render() {
return (
<Block flex>
<Block row space="evenly">
<Block center>
<Input
placeholder='Code article'
onChangeText={data => this.setState({ Input_ITMREF: data })}
ref={this.myRef}
/>
</Block>
</Block>
<Block center>
<Button style={styles.button} onPress={() => this.props.navigation.navigate('Barcode')}>Barcode</Button>
<Text style={{ margin: 10 }}>Post: {this.props.navigation.getParam('itmref')}</Text>
</Block>
</Block>
);
}
}
And Barcode.js
import React, {} from 'react';
import { ScrollView, TouchableWithoutFeedback, Dimensions, StyleSheet, FlatList, View, Alert, TouchableOpacity, TextInput } from 'react-native';
import { BarCodeScanner } from 'expo-barcode-scanner';
import { Button } from "../components/";
export default class Barcode extends React.Component {
static navigationOptions = {
header: null //hide the header bar
};
handleBarCodeScanned = ({ type, data }) => {
this.props.navigation.navigate("Stock", {
itmref: data
});
};
render() {
return (
<BarCodeScanner
onBarCodeScanned={this.handleBarCodeScanned}
style={styles.barcodeScanner}
/>
);
}
}
You can pass a state handler function as prop to Barcode screen and use that to set value for textInput in state.
in Stock(in state)
state = {
inputValue: ''
}
....
const setInputTextValue= (newValue) => {
this.setState({
inputValue: newValue
})
you pass this function as prop to Barcode scene and call it whenever you wanna set a new value(considering Stock scene is still mounted).
UPDATE: What is the proper way to update a previous StackNavigator screen?
also another solution i just saw: Updating State of Another Screen in React Navigation
You need to use WillFocus method(Included in react-navigation) when you comeback from Barcodepage to stockPage
componentDidMount(){
console.log("willFocus runs") initial start
const {navigation} = this.props;
navigation.addListener ('willFocus', async () =>{
console.log("willFocus runs") // calling it here to make sure it is logged at every time screen is focused after initial start
});
}
For More Information read this document
https://reactnavigation.org/docs/function-after-focusing-screen/

React Native: How to take ScreenShot Programmatically and save it to the gallery

I am not able to find any documents that explain this feature.
I would like to know how to take screenshot programmatically and save it to the gallery.
I tried the react-native-view-show gallery but it doesn't work.
Any help would be appreciated
Current Code :
import React, { Component } from "react";
import { View, Text, Button, Image, ScrollView, StatusBar } from "react-native";
import { captureScreen } from "react-native-view-shot";
import CameraRollExtended from "react-native-store-photos-album";
export default class App extends Component {
componentDidMount() {
StatusBar.setHidden(true, "none");
}
render() {
return (
<View>
<Button
title="Click"
onPress={() => {
captureScreen({
format: "jpg",
quality: 0.8
}).then(
uri => {
CameraRollExtended.saveToCameraRoll(
{
uri: uri,
album: "Name"
},
"photo"
);
},
error => console.error("Oops, snapshot failed", error)
);
}}
/>
</View>
);
}
}

Unable to load provider from react-redux module in react native

I am creating a slide bar, In that, I have used the react-redux library. When I call the class which contains the redux-code, it works fine. I want to show this slide bar after login. Therefore, with conditions (I set a state variable if user login successfully then only this page should get rendered), I tried to call the same file which shows a blank page. I printed the console log. I am able to print all the logs. But with conditions, I am not able to load the data.
I don't know much about react-redux.Can you assist me to resolve this?
My code is,
main.js,
import React, {Component} from 'react';
import {
StyleSheet,
Dimensions,
Platform,
View,
StatusBar,
DrawerLayoutAndroid,
} from 'react-native';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducer from '../Redux/reducers';
import { setNavigator, setActiveRoute } from "../Redux/actions";
import DrawerContent from '../Navigation/DrawerContent';
import Toolbar from '../Navigation/Toolbar';
import AppNavigation from '../Navigation/AppNavigation';
import { bgStatusBar, bgDrawer } from '../global.styles';
let store = createStore(reducer);
/* getDrawerWidth Default drawer width is screen width - header width
* https://material.io/guidelines/patterns/navigation-drawer.html
*/
const getDrawerWidth = () => Dimensions.get('window').width - (Platform.OS === 'android' ? 56 : 64);
export default class Main extends Component {
constructor() {
super();
this.drawer = React.createRef();
this.navigator = React.createRef();
}
componentDidMount() {
store.dispatch(setNavigator(this.navigator.current));
}
openDrawer = () => {
this.drawer.current.openDrawer();
};
closeDrawer = () => {
this.drawer.current.closeDrawer();
};
getActiveRouteName = navigationState => {
if (!navigationState) {
return null;
}
const route = navigationState.routes[navigationState.index];
// dive into nested navigators
if (route.routes) {
return getActiveRouteName(route);
}
return route.routeName;
};
render() {
return (
<Provider store={store}>
<DrawerLayoutAndroid
drawerWidth={getDrawerWidth()}
drawerPosition={DrawerLayoutAndroid.positions.Left}
renderNavigationView={
() => <DrawerContent closeDrawer={this.closeDrawer} />
}
ref={this.drawer}
>
<View style={styles.container}>
<StatusBar
translucent
animated
/>
<Toolbar showMenu={this.openDrawer} />
<AppNavigation
onNavigationStateChange={(prevState, currentState) => {
const currentScreen = this.getActiveRouteName(currentState);
store.dispatch(setActiveRoute(currentScreen));
}}
ref={this.navigator}
/>
</View>
</DrawerLayoutAndroid>
</Provider>
);
}
}
Login.js
import Main from './main';
render() {
return (
<View>
{this.state.isLoggedIn ?
<Main/>
:
<ChangePassword isUpdatePassword={this.state.isUpdatePassword} callLogin={this.callLogin}/>
);
}
}
If I just call Main class inside render method it works. But It does not work with conditions.

What is the best way to deal with empty error message in react native?

I have to display a error message called "No record available".
This is my scenario : -
API Call {
if (data){
loading == false
}
}
In my component
Render(){
{
data.length > 0 && this.state.loading == false ?
<Flat List/>
: null
}
{
data.length==0 ?
<Text>No Record found</Text>
: null
}
}
My Problem was , my message displays if data not found but it doesn't refresh.
I have to achieve a scenario like this -
when i open or navigate through a page then its first show blank then loader start and after API call if data not found then they display a message.
This is a working example of what you describe. When the component loads the data is empty until your API call runs in componentDidMount. I have emulated the API call with a timeout of 2 seconds. You need to switch out the setTimeout function in apiCall with your own fetch method and set the state in the callback of that function
import React, { Component } from 'react';
import { View, Text } from 'react-native';
class Test extends Component {
state = {
loading: false,
data: [],
};
componentDidMount() {
this.apiCall();
}
apiCall = () => {
this.setState({ loading: true });
setTimeout(() => {
this.setState({
loading: false,
data: ['1', '2', '3'],
});
}, 3000);
};
render() {
if (this.state.loading) return <Text>Loading...</Text>;
if (this.state.data.length === 0) return <Text>No records found</Text>;
return (
<View>
<Text>Records found</Text>
</View>
);
}
}
export default Test;
You can bind your action and reducers data
here this is the example you want
import React, { Component } from 'react';
import {
Text,
View,
Dimensions,
FlatList,
ScrollView,
} from 'react-native';
import { connect } from 'react-redux';
import {showLoading, getProducts} from '../actions/productAction';
import * as Progress from 'react-native-progress';
class Data extends Component {
this.state = {
product: [],
loading: false
};
componentWillMount() {
this.setState({loading: true});
API CALL();
}
render() {
return (
<View>
{this.state.isLoading ?
<View>
<Progress.CircleSnail thickness={5} size={55} color={['#000000', '#000000', '#FFFFFF',]} />
</View>
:
null
}
{this.state.product.length === 0 && <View>
<Text>{"NO PRODUCT"}</Text>
</View>}
<FlatList
data={this.state.product}
/>
</View>
);
}
}
export default Data;

react-native redux props changes back to undefined

I'm trying to add a filter to my app, but for some reason selectedValue in the <Picker> component doesn't stick with the option I select. I can see the filter text changing from "all" to "lobby" in the top left, however as soon as the player list fully renders, it changes back to "all." and playerListFilterType prop is set to undefined. I stepped through the code in a debugger, and it stays "lobby" until the list re-renders. The action itself works, so the list is showing accurate results.
Here's what my code looks like:
import React from 'react'
import { View, Picker } from 'react-native'
import PlayerList from '../components/PlayerList'
import { fetchPlayerListAsync, filterPlayers } from '../redux/actions/player_actions';
import NavigationHeaderTitle from '../components/NavigationHeaderTitle'
import PlayerStatusFilterPicker from '../components/pickers/PlayerStatusFilterPicker'
import { connect } from 'react-redux'
class PlayerListScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
const playerStatusFilterPicker = (
<PlayerStatusFilterPicker
playerListFilterType={navigation.getParam('playerListFilterType')}
filterPlayers={navigation.getParam('filterPlayers')}
playerList={navigation.getParam('playerList')}
/>
)
return {
headerTitle: navigation.getParam('headerButton'),
headerRight: playerStatusFilterPicker
}
}
async componentDidMount() {
await this.fetchPlayersAsync();
}
setNavigationParams = () => {
this.props.navigation.setParams({
headerButton: this.headerButton,
playerList: this.props.playerList,
playerListFilterType: this.props.playerListFilterType,
filterPlayers: this.props.filterPlayers
})
}
// navigation header element
headerButton = () => (
<NavigationHeaderTitle
handleDataRequest={this.fetchPlayersAsync}
titleMessage={(this.props.fetchingData) ? 'fetching list of players' : `${this.props.playerList.length} online`}
/>
)
fetchPlayersAsync = async () => {
await this.props.fetchPlayerListAsync();
this.setNavigationParams()
}
render() {
return (
<View>
<PlayerList
playerList={this.props.playerList}
fetchingData={this.props.fetchingData}
handleDataRequest={this.fetchPlayersAsync}
/>
</View>
)
}
}
const mapStateToProps = state => {
return {
fetchingData: state.player.fetchingData,
playerList: state.player.playerList,
unfilteredPlayerList: state.player.unfilteredPlayerList,
playerListFilterType: state.player.playerListFilterType
}
};
export default connect(mapStateToProps, { fetchPlayerListAsync, filterPlayers })(PlayerListScreen)
and here's what the filter component looks like, but I don't think the problem lies here:
import React, { Component } from "react";
import {
View,
Picker
} from "react-native";
import * as constants from '../../constants'
class PlayerStatusFilterPicker extends Component {
render() {
return (
<View>
<Picker
selectedValue={this.props.playerListFilterType}
onValueChange={(itemValue) => this.props.filterPlayers(itemValue, this.props.playerList)}
style={{ height: 40, width: 100 }}
>
<Picker.Item label='all' value='all' />
<Picker.Item label="lobby" value={constants.IN_LOBBY} />
<Picker.Item label="in game" value={constants.IN_GAME} />
</Picker>
</View>
);
}
}
export default PlayerStatusFilterPicker;
Here's what the reducer looks like:
// show only the players that are waiting in the main lobby
case actionTypes.SHOW_PLAYERS_IN_LOBBY: {
const filteredList = action.payload.filter(player => player.status === constants.IN_LOBBY)
return { playerList: filteredList, playerListFilterType: constants.IN_LOBBY, fetchingData: false }
}
// show only the players that are currently playing
case actionTypes.SHOW_PLAYERS_IN_GAME: {
const filteredList = action.payload.filter(player => player.status === constants.IN_GAME)
return { playerList: filteredList, playerListFilterType: constants.IN_LOBBY, fetchingData: false }
}
Fixed it by using componentDidUpdate lifecycle method. Like so:
componentDidUpdate(prevProps) {
if (this.props.playerListFilterType != prevProps.playerListFilterType) {
this.props.navigation.setParams({
playerListFilterType: this.props.playerListFilterType
})
}
}