I'm migrating my old project (from RN 0.57) to latest one (0.62.2), but I'm having some troubles with react-navigation v5.
I have one screen called "Race" where I have racers and their times. When user click in one specific racer, a new screen (called 'PersonalRank') are open with data from this particular racer.
This second screen takes tx_id as parameter to get data from redux/state. The problem is that not always this data is being passed to screen. I have made a little video to show the problem in simulator (the same happens on real smartphone). As you can see in log screen, the tx_i is correct while calling the new screen.....but is not always changing in the props of new screen.
This is the URL of demo video: https://youtu.be/pjhH6pOMRhs (1:55 minute video).
In the races list, I'm using this code to call the second screen (note console.log in first line):
return (
<ListItem onPress={() => { console.log("Navegando para o TXID:", tx_id); this.props.navigation.navigate("PersonalRank",{tx_id}); } } NoIndent style={{width: '100%', backgroundColor: 'white', marginTop: 0, paddingTop: 0, marginBottom: 0, paddingBottom: 5, marginLeft: 0, paddingLeft: 0, paddingRight: 0, marginRight: 0}}>
<Grid>
<Col size={10}>
<Row><Text style={{paddingLeft: 4}}>#{position}</Text></Row>
{temperature > 0 && settings.disp_temp == true &&
<Row><Text style={{fontSize: 10, paddingLeft: 4, color: temp_color, fontWeight: 'bold'}}>{temperature}ºC</Text></Row>
}
{speed > 0 &&
<Row><Text style={{fontSize: 10, paddingLeft: 4, fontWeight: 'bold'}}>{speed.toFixed(2)} Km/h</Text></Row>
}
</Col>
<Col size={90}>
<Row>
<Col size={67}><Text style={{alignSelf: 'flex-start', fontWeight: 'bold', fontSize: 14}}>{getPilotName(tx_id)} (ID:{tx_id})</Text></Col>
<Col size={33}><Text style={{fontSize: 10, alignSelf: 'flex-end', marginRight: 3}}>{translate('total_time',{...i18n_opt})}: {msToTime(total_time)}</Text></Col>
</Row>
<Row>
<Col><Text style={styles.itemHeaderText}>{translate('laps',{...i18n_opt})}</Text></Col>
<Col><Text style={styles.itemHeaderText}>{translate('last_lap',{...i18n_opt})}</Text></Col>
<Col><Text style={styles.itemHeaderText}>{translate('best_lap',{...i18n_opt})}</Text></Col>
<Col><Text style={styles.itemHeaderText}>{translate('best_time',{...i18n_opt})}</Text></Col>
<Col><Text style={[styles.itemHeaderText,styles.itemAlignRight, {marginRight: 5}]}>{translate('diff',{...i18n_opt})}</Text></Col>
</Row>
<Row>
<Col><Text style={styles.itemValueText}>{laps.length}</Text></Col>
<Col><Text style={[styles.itemValueText, {fontWeight: 'bold'}]}>{msToTime(last_time)}</Text></Col>
<Col><Text style={styles.itemValueText}>{best_lap}</Text></Col>
<Col><Text style={[styles.itemValueText, { color: '#90db18', fontWeight: 'bold' }]}>{msToTime(best_time)}</Text></Col>
<Col><Text style={[styles.itemValueText,styles.itemAlignRight, {marginRight: 5}]}>{diff}</Text></Col>
</Row>
{settings.show_estimate_pg &&
<Row>
<Col><ProgressBar style={styles.progress} progress={pg} width={null} /></Col>
</Row>
}
</Col>
</Grid>
</ListItem>
);
Now, in the PersonalRank screen, I'm getting tx_id in render() function...but sometime is doesn't work (even waiting for a while) (see video). The screen opens, but with the last opened tx_id.....sometimes it opens and after a few seconds, it changes.
render() {
const { pilots, settings } = this.props;
const { dispatchDelPilot } = this.props;
const tx_id = this.props.route.params.tx_id;
const name = getPilotName(tx_id);
const { dispatchDeleteLap, dispatchSplitLap } = this.props;
//console.log("ROUTE: ",this.props.route);
console.log("TX id (from props): ",tx_id);
const { race } = this.props;
let laps = [];
let pilot = "";
var laps_out = [];
var last_time = 0;
var fastest_time = 999999999;
var fastest_index = 0;
var last_speed = 0;
var total_time = 0;
for (var i = 0; i < race.length; i++) {
if (race[i].tx_id == tx_id) {
pilot = race[i].pilot;
laps = race[i].laps;
total_time = race[i].total_time;
break;
}
}
if (laps.length <= 0) {
//this.props.navigation.navigate("CurRace");
return <Text>No laps for this racer.</Text>;
}
let chart_values = [];
let chart_temperature_values = [];
let chart_labels = [];
let has_speed = false;
let has_bat = false;
//console.log("Laps: ",laps);
laps.map((item, index) => {
if (settings.show_pers_chart) {
chart_labels.push(index + 1);
chart_values.push(msToTime(item.time));
if ("temperature" in item) {
if (item.temperature > 0) {
}
}
}
if ("speed" in item) {
if (item.speed > 0) {
if (item.speed > last_speed) {
last_speed = item.speed;
}
has_speed = true;
}
}
if ("battery" in item) {
if (item.battery > 0) {
has_bat = true;
}
}
if (item.time > last_time) {
item.diff = "+" + msToTimeDiff(item.time - last_time);
item.diff_style = { color: "#ea2b12" };
last_time = item.time;
} else if (item.time < last_time) {
item.diff = "-" + msToTimeDiff(last_time - item.time);
item.diff_style = { color: "#12ea5a" };
last_time = item.time;
} else {
item.diff = "0";
last_time = item.time;
item.diff_style = null;
}
if (last_time < fastest_time) {
fastest_index = index;
fastest_time = last_time;
}
// Clear fastest style
item.fastest_style = null;
// Key
item.key = index.toString();
});
laps[0].diff = "0";
let has_temp = chart_temperature_values.length > 0 ? true : false;
laps[fastest_index].fastest_style = { color: "#12ea5a" };
const laps2 = laps;
const renderHiddenItem = (data, rowMap) => (
<View style={styles.rowBack}>
<Text>Left</Text>
<TouchableOpacity
style={[styles.backRightBtn, styles.backRightBtnLeft]}
onPress={() => {
this.setState({
...this.state,
visibleModal: true,
cur_tx_id: tx_id,
cur_lap: data.item.lap
});
}}
>
<MaterialCommunityIcons
name="arrow-split-vertical"
size={20}
color="#5e69d9"
/>
</TouchableOpacity>
<TouchableOpacity
style={[styles.backRightBtn, styles.backRightBtnRight]}
onPress={() => {
if (laps.length > 1) {
console.log("Data: ",data.item);
console.log("Apagando tx_id: ",tx_id,", Volta: ",data.item.lap,", Show alerts: ",settings.show_alerts);
dispatchDeleteLap(
tx_id,
data.item.lap,
settings.show_alerts
);
} else {
alert(
translate("cant_del_single_lap", { ...i18n_opt })
);
}
}}
>
<Icon active name="trash" size={20} />
</TouchableOpacity>
</View>
);
return (
<Container style={styles.container}>
<Header>
<Left>
<Button transparent onPress={() => this.props.navigation.goBack()}>
<Icon name="arrow-back" />
</Button>
</Left>
<Body>
<Title>{name}</Title>
</Body>
<Right>
<Button
transparent
onPress={() =>
this.props.navigation.navigate("RCharts", { tx_id })
}
>
<Icon type="MaterialCommunityIcons" name="chart-bar" />
</Button>
</Right>
</Header>
<Modal
isVisible={this.state.visibleModal === true}
animationIn={"slideInLeft"}
animationOut={"slideOutRight"}
>
{this._renderModalContent()}
</Modal>
<View style={{ flex: 1 }}>
<View style={{ height: "100%" }}>
<View style={{ flex: 8 }}>
<ScrollView>
<ViewShot ref="PersonalRankShot">
<View style={styles.line} collapsable={false}>
<View style={{ flex: 3 }}>
<Text style={[styles.alignCen, styles.headerTitle]}>
{translate("lap", { ...i18n_opt })}
</Text>
</View>
{chart_temperature_values.length > 0 && (
<View style={{ flex: 4 }}>
<Text style={[styles.alignCen, styles.headerTitle]}>
{translate("temperature", { ...i18n_opt })}
</Text>
</View>
)}
<View style={{ flex: 9 }}>
<Text style={[styles.alignCen, styles.headerTitle]}>
{translate("time", { ...i18n_opt })}
</Text>
</View>
{has_speed && (
<View style={{ flex: 4 }}>
<Text style={[styles.alignCen, styles.headerTitle]}>
{translate("speed", { ...i18n_opt })}
</Text>
</View>
)}
{has_bat && (
<View style={{ flex: 4 }}>
<Text style={[styles.alignCen, styles.headerTitle]}>
{translate("bat", { ...i18n_opt })}
</Text>
</View>
)}
<View style={{ flex: 4 }}>
<Text
style={[
{ paddingRight: 10 },
styles.alignRig,
styles.headerTitle
]}
>
{translate("diff", { ...i18n_opt })}
</Text>
</View>
</View>
<View style={styles.container}>
<SwipeListView
data={laps2}
disableRightSwipe={true}
renderItem={(data, rowMap) => (
<View style={styles.rowFront}>
<PersonalRankItem
dados={data.item}
settings={settings}
hasTemp={has_temp}
hasSpeed={has_speed}
hasBat={has_bat}
/>
</View>
)}
renderHiddenItem={renderHiddenItem}
leftOpenValue={75}
rightOpenValue={-90}
previewRowKey={'0'}
previewOpenValue={-40}
previewOpenDelay={3000}
//onRowDidOpen={onRowDidOpen}
/>
</View>
</ViewShot>
</ScrollView>
</View>
</View>
</View>
<TouchableOpacity
style={styles.bt_share}
onPress={() => this._captureChart()}
>
<Icon name="share" size={20} color="#ffffff" />
</TouchableOpacity>
</Container>
);
}
That am I missing here?
This used to work in old version of RN and react-navigation (4) without any problem.
Related
I am having trouble updating an array that is passed as a prop into my child component. I have searched around but haven't found an answer that can directly solve my problem. My code is as follows:
App.js
import React, { Component } from 'react';
import { StyleSheet, Text, View, SafeAreaView } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import AddMedication from "./src/AddMedication";
import MedicationList from './src/MedicationList';
const Stack = createNativeStackNavigator();
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
medications: [],
}
this.addMedication = this.addMedication.bind(this);
}
addMedication = (name, dosage, measurement, timesDaily) => {
console.log("Medication added.")
var newItem = {name: name, dosage: dosage, measurement: measurement, timesDaily: timesDaily}
this.setState({
medications: [...this.state.medications, newItem]
})
}
render() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Medication List">
{(props) => <MedicationList {...props} medications={this.state.medications} />}
</Stack.Screen>
<Stack.Screen name="Add New Medication">
{(props) => <AddMedication {...props} addMedication={this.addMedication} />}
</Stack.Screen>
</Stack.Navigator>
</NavigationContainer>
);
}
}
This is the home screen where I am trying to display the array but nothing shows up
MedicationList.js
class MedicationList extends Component {
constructor(props) {
super(props);
this.state = {
tableHead: ['Name', 'Dosage', 'Times Daily', 'Prescriber', 'For Diagnosis'],
}
}
medication = ({ item }) => {
<View style={{ flexDirection: 'row' }}>
<View style={{ width: 50, backgroundColor: 'lightyellow'}}>
<Text style={{ fontSize: 16, fontWeight: 'bold', textAlign: 'center'}}>{item.name}</Text>
</View>
<View style={{ width: 400, backgroundColor: 'lightpink'}}>
<Text style={{ fontSize: 16, fontWeight: 'bold' , textAlign: 'center'}}>{item.dosage}{item.selectedMeasurement}</Text>
</View>
<View style={{ width: 400, backgroundColor: 'lavender'}}>
<Text style={{ fontSize: 16, fontWeight: 'bold' , textAlign: 'center'}}>{item.timesDaiy}</Text>
</View>
</View>
}
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', marginTop: '10%'}}>
<Button
title="+ Add New Medication"
onPress={() => {
/* 1. Navigate to the Details route with params */
this.props.navigation.navigate('Add New Medication', {
medications: this.props.medications,
});
}}
/>
<FlatList
data={this.props.medications}
renderItem={this.medication}
/>
</View>
);
}
}
This is where I click the add button to update the medications array
AddMedication.js
class AddMedication extends Component {
constructor(props) {
super(props);
this.state = {
name: '',
dosage: 0,
selectedMeasurement: "mg",
timesDaily: '',
prescriber: '',
forDiagnoses: '',
instructions: '',
validity: false,
};
}
setName = (name) => {
let isValid = this.isFormValid();
this.setState({ name: name, validity: isValid });
}
setDosage = (dosage) => {
let isValid = this.isFormValid();
this.setState({ dosage: dosage, validity: isValid });
}
setMeasurement = (measurement) => {
this.setState({ selectedMeasurement: measurement });
}
setTimesDaily = (timesDaily) => {
let isValid = this.isFormValid();
this.setState({ timesDaily: timesDaily, validity: isValid });
}
setPrescriber = (prescriber) => {
this.setState({ prescriber: prescriber });
}
setDiagnoses = (diagnoses) => {
this.setState({ forDiagnoses: diagnoses });
}
setInstructions = (instructions) => {
this.setState({ instructions: instructions });
}
isFormValid = () => {
return (this.state.name !== '' && (this.state.dosage !== '' && this.state.dosage > 0)
&& (this.state.timesDaily !== '' && this.state.timesDaily > 0));
}
render() {
return (
<View style={styles.container}>
<Text style={{color: 'red', marginBottom: 5, marginLeft: -125}}>* denotes required field</Text>
<View style={{flexDirection: 'row'}}>
<Text style={styles.required}>*</Text>
<TextInput
style={styles.inputText}
onChangeText={(name) => this.setName(name)}
placeholder="Medication Name"
value={this.state.name}
/>
</View>
<View style={{flexDirection: 'row'}}>
<Text style={styles.required}>*</Text>
<TextInput
style={styles.inputText}
onChangeText={(dosage) => this.setDosage(dosage)}
placeholder="Dosage"
value={this.state.dosage}
/>
</View>
<View style={styles.dosageContainer}>
<Text style={{flex: 1, marginTop: 100, marginLeft: 30}}>
Select Measurement:
</Text>
<Picker
style={styles.picker}
selectedValue={this.state.selectedMeasurement}
onValueChange={(itemValue, itemIndex) =>
this.setMeasurement(itemValue)
}>
<Picker.Item label="mg" value="mg" />
<Picker.Item label="g" value="g" />
<Picker.Item label="ml" value="ml" />
</Picker>
</View>
<View style={{flexDirection: 'row'}}>
<Text style={styles.required}>*</Text>
<TextInput
style={styles.inputText}
onChangeText={(timesDaily) => this.setTimesDaily(timesDaily)}
placeholder="Times daily"
value={this.state.timesDaily}
/>
</View>
<TextInput
style={styles.inputText}
onChangeText={(prescriber) => this.setPrescriber(prescriber)}
placeholder="Prescriber"
value={this.state.prescriber}
/>
<TextInput
style={styles.inputText}
onChangeText={(diagnoses) => this.setDiagnoses(diagnoses)}
placeholder="For diagnoses"
value={this.state.forDiagnoses}
/>
<TextInput
style={styles.inputText}
onChangeText={(instructions) => this.setInstructions(instructions)}
placeholder="Instructions"
value={this.state.instructions}
/>
<TouchableOpacity
style={this.isFormValid() ? styles.validButton : styles.invalidButton}
disabled={!Boolean(this.state.name && this.state.dosage && this.state.timesDaily)}
onPress={() => {
this.props.navigation.goBack()
this.props.addMedication(this.state.name, this.state.dosage,
this.state.selectedMeasurement, this.state.timesDaily)
}}
>
<Text style={{color: 'white'}}>Add Medication</Text>
</TouchableOpacity>
</View>
)
}
}
You can pass the state value but I think you cannot pass the addMedication method just like this.
Could you please try passing an arrow function that uses the setState method?
For example:
<Stack.Screen name="Add New Medication">
{(props) => <AddMedication {...props} addMedication={(name, dosage, measurement, timesDaily)=> {this.addMedication(name, dosage, measurement, timesDaily)}} />}
</Stack.Screen>
THE PROBLEM
see the screenshot of probleme
//below code is which loades the slider..you can also find the below snippet in ClassFlatListRender.js , when id of element passed through FlatList which equals to the selected Id then the audio slider should work, but if I trigger one audio file element all of the sliders are moving
{id == this.state.selectedOptionId ?
(<View style={[styles.viewBar, { flexDirection: 'row', }]}>
<View style={[styles.viewBarPlay, { width: playWidthMessage }]} />
</View)
:
<View><Text>no slider<Text></View>}
THE CODE
The structure of my code is this: I have a container component with all the logic and state,
which contains a FlatList component , which again contains a custom presentational List.
Container
Custom list component that includes the FlatList component and the renderItem method
invokes classFlatList component (presentational, stateless)
The container includes this component
<CustomList
items={this.state.data}
/>
CustomList:
export class CustomList extends Component {
render() {
const renderItem = ({ item }) => {
return (
<ClassFlatListRender
message={item.message}
id={item.id}
user={item.user}
timestamp={item.timestamp}
heights={item.heights}
url={item.url}
audioLength={item.audioLength}
type={item.type}
/>
)
}
let { items } = this.props
return (
<View style={styles.container}>
< FlatList
style={styles.container}
data={items.slice().sort((a, b) => b.timestamp - a.timestamp)}
// data={data}
renderItem={renderItem}
inverted={true}
extraData={items}
keyExtractor={(items) => `${items.timestamp}`}
/>
</View>
)
}
}
The ClassFlatListRender.js
export class ClassFlatListRender extends React.Component {
constructor() {
super();
this.state = {
update: false,
currentPositionSec: 0,
currentDurationSec: 0,
playTime: '00:00:00',
duration: '00:00:00',
id: '',
tempId: '',
selectedOptionId: '',
setId: '',
};
this.audioRecorderPlayer = new AudioRecorderPlayer();
this.audioRecorderPlayer.setSubscriptionDuration(0.09); // optional. Default is 0.1
}
onStartPlay = async (id, url) => {
console.log('onStartPlay');
//? Custom path
// const msg = await this.audioRecorderPlayer.startPlayer(this.path);
//? Default path
console.log("arvinf", url)
const msg = await this.audioRecorderPlayer.startPlayer(url);
console.log("msg", msg)
const volume = await this.audioRecorderPlayer.setVolume(1.0);
console.log(`file: ${msg}`, `volume: ${volume}`);
this.audioRecorderPlayer.addPlayBackListener((e) => {
this.setState({
currentPositionSec: e.currentPosition,
currentDurationSec: e.duration,
// playWidth: (e.currentPosition / e.duration) * (screenWidth - 300)
// playTime: this.audioRecorderPlayer.mmssss(
// Math.floor(e.currentPosition),
// ),
// duration: this.audioRecorderPlayer.mmssss(Math.floor(e.duration)),
});
});
};
onPausePlay = async () => {
await this.audioRecorderPlayer.pausePlayer();
};
renderElement = (id, audioLength) => {
if (this.state.selectedOptionId == id) {
// this.onStartPlay(url)
return (
<Fcon name={this.state.selectedOptionId == id ? 'play' : 'pasue'} size={24} color={'white'} />
)
}
if (this.state.selectedOptionId !== id || this.state.currentPositionSec == this.state.duration) {
// this.onPausePlay(url)
return (
<Fcon name={'play'} size={24} color={'white'} />
)
}
}
checkFunction = async (id, url) => {
console.log("id: before", id)
await this.setState({
selectedOptionId: id,
setId: id
})
console.log('id:after', this.state.selectedOptionId)
id == this.state.setId ? this.onStartPlay(id, url) : this.onPausePlay()
// return this.state.selectedOptionId
}
render() {
let { id, user, url, heights, message, audioLength, type } = this.props;
let playWidthMessage =
(this.state.currentPositionSec / this.state.currentDurationSec) *
(screenWidth - 300);
if (!playWidthMessage) {
playWidthMessage = 0;
}
return (
<View id={id}>
{
url.endsWith(".jpg") ?
< View key={id} style={[user == 1 ? styles.receiver : styles.sender, { paddingHorizontal: 14, paddingVertical: 8, borderRadius: heights > 50 ? 20 : 50 }]}>
<View style={{ alignItems: 'center', }}>
{console.log("address", url)}
<Image
resizeMode="contain"
// resizeMethod="scale"
style={{ width: 200, height: 200 }}
source={{ uri: url, width: 200, height: 200 }}
// source={{ uri: "https://picsum.photos/200", width: 200, height: 200 }}
// source={require("https://ibb.co/hM8BbY5")}
/>
</View>
</View> : null
}
{
url.endsWith(".mp3") ?
(
<View key={this.props.id} style={[user == 1 ? styles.receiver : styles.sender, { paddingHorizontal: 14, paddingVertical: 8, borderRadius: heights > 50 ? 20 : 50 }]}>
<View style={[styles.viewPlayer, { backgroundColor: user == 1 ? darkgrey : '#0096FF', alignSelf: 'center', borderRadius: 50, flexDirection: 'row', justifyContent: "center", alignItems: 'center' }]}>
{/* line */}
<TouchableOpacity activeOpacity={0.5} onPress={() => {
this.checkFunction(id, url)
}} style={{ borderRadius: 50, width: 36, height: 36, justifyContent: 'center', alignItems: 'center' }}>
<View>
{this.renderElement(id, url, audioLength)}
</View>
</TouchableOpacity>
<View >
<TouchableOpacity
style={[styles.viewBarWrapper, { width: screenWidth - 300 }]}
onPress={this.onStatusPress}
>
{id == this.state.setId ?
(<View style={[styles.viewBar, { flexDirection: 'row', }]}>
{/* {console.log("playWidth:", this.state.playWidth)} */}
<View style={[styles.viewBarPlay, { width: playWidthMessage }]} />
</View>
)
: <View><Text>ddd</Text></View>}
</TouchableOpacity>
</View>
<Text style={[styles.txtRecordCounter, { color: 'white' }]}>{audioLength}</Text>
</View>
</View >) : null
}
{
type == "txt" ?
// && user == 1 ?
<View key={id} >
<TouchableOpacity onPress={async () => await this.setState({ tempId: id })} style={[styles.receiver, { borderRadius: heights > 50 ? 20 : 50, backgroundColor: this.state.tempId == id ? 'pink' : 'yellow' }]}>
<Text style={styles.receiverText}>{message}</Text>
</TouchableOpacity>
</View> : null
}
</View >
)
}
}
Maintain the selected audio id in your parent component, pass it to child components using props, animate based on the condition. in ur message component
I'm new to react native world and I'm trying to integrate a calendar with a time slot picker, so I'm trying to pass the selected date from the calendar to the slot picker page but I'm having this Error when I press on a date in the calendar and I couldn't fix it.
TypeError: undefined is not an object (evaluating 'navigation.navigate')
This is my calendar function:
const RequestMeeting = ({ navigation }) => {
const [isModalVisible, setModalVisible] = useState(false);
const toggleModal = () => {
setModalVisible(!isModalVisible);
};
return (
<View style={{ margin: 100, }}>
<Button title="Show modal" onPress={toggleModal} />
<Modal isVisible={isModalVisible} avoidKeyboard={true} scrollHorizontal={true} propagateSwipe={true}>
<ScrollView>
<View style={{ margin: 50, backgroundColor: 'gray', borderRadius: 20, padding: 20, margin: 20 }}>
<Text style={styles.heading}>Request to Buy/Rent</Text>
<View style={{ paddingBottom: 10 }}></View>
<View >
<Calendar
onDayPress={(day) => navigation.navigate("Slot", { bookingDate: day })}
style={styles.calendar}
hideExtraDays
theme={{
selectedDayBackgroundColor: 'green',
todayTextColor: 'green',
arrowColor: 'green',
}}
/>
</View>
<Button
buttonStyle={styles.register}
title="Send Buy/Rent request"
/>
<Button
buttonStyle={styles.cancelbtn}
title="Cancel"
onPress={toggleModal}
/>
</View>
</ScrollView>
</Modal>
</View>
);
};
And this is my time slot picker function:
const jsonData = {
"slots": {
"slot1": "9:00am to 9:30am",
"slot2": "9:30am to 10:00am",
"slot3": "10:00am to 10:30am",
"slot4": "10:30am to 11:00am",
"slot5": "11:00am to 11:30am",
"slot6": "11:30am to 12:00pm"
}
}
const Slot = ({ navigation }) => {
const onPressBack = () => {
const { goBack } = navigation
goBack()
}
const slots = jsonData.slots
const slotsarr = Object.keys(slots).map(function (k) {
return (
<View key={k} style={{ margin: 5 }}>
<TouchableOpacity >
<Text>{slots[k]}</Text>
</TouchableOpacity>
</View>)
});
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" />
<View >
<TouchableOpacity onPress={() => onPressBack()}><Text >Back</Text></TouchableOpacity>
<Text ></Text>
<Text ></Text>
</View>
{ slotsarr}
</View>
);
}
This error means that wherever you are rendering RequestMeeting or Slot (whichever one has the error) it's not getting the navigation prop. If it is rendered as a Screen then it will get the prop from the Navigator. If it's not a top-level screen then you need to pass down the prop from a parent or access it with the useNavigation hook.
Docs: Access the navigation prop from any component
I have a functional component Login Screen and have 4 Input Fields in them, along with a login button.
Here is the Full Code for my component Screen:
export default function Login() {
//Configs
const navigation = useNavigation();
const orientation = useDeviceOrientation();
const { colors, dark } = useTheme();
const InputTheme = {
colors: {
placeholder: colors.accent,
primary: colors.accent,
error: "red",
},
};
/** State Codes */
//States
const [login, setLogin] = useState({
email: "",
password: "",
licenseKey: "",
deviceName: "",
});
const [loading, setLoading] = useState(false);
const [secureEntry, setSecureEntry] = useState(true);
//Errors
const [errorEmail, setEmailError] = useState(false);
const [errorPWD, setPWDError] = useState(false);
const [errorLicense, setLicenseError] = useState(false);
const [errorDevice, setDeviceError] = useState(false);
//Error Messages
const [messageEmail, setEmailMessage] = useState("Looks Good");
const [messagePWD, setPWDMessage] = useState("All Good");
async function VerifyInputs() {
var pattern = /^[a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)*#[a-z0-9]+(\-[a-z0-9]+)*(\.[a-z0-9]+(\-[a-z0-9]+)*)*\.[a-z]{2,4}$/;
if (login.email == "") {
//Email cannot be empty
setEmailMessage("Email cannot be Blank!");
setEmailError(true);
return;
} else if (login.email != "" && !pattern.test(login.email)) {
//Email is not valid
setEmailMessage("This is not a valid email address!");
setEmailError(true);
return;
} else {
console.log("resolved email");
setEmailMessage("");
setEmailError(false);
}
if (login.password == "") {
//Password cannot be empty
setPWDMessage("Password cannot be Empty!");
setPWDError(true);
return;
} else if (login.password.length < 5) {
//Password must be minimum 5 characters.
setPWDMessage("Password must be of minimum 5 characters!");
setPWDError(true);
return;
} else {
console.log("resolved password");
setPWDMessage("");
setPWDError(false);
}
if (login.licenseKey == "") {
//License Key can't be Empty
setLicenseError(true);
return;
} else {
console.log("License resolved");
setLicenseError(false);
}
if (login.deviceName == "") {
//Device Name can't be empty as well
setDeviceError(true);
return;
} else {
console.log("Device name resolved");
setDeviceError(false);
}
Toast.show("Validation Successful");
}
function MobileContent() {
console.log("mobile_content rerendered");
return (
<View style={{ flex: 1, backgroundColor: colors.accent }}>
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}></View>
<View style={{ flex: 3, justifyContent: "center" }}>
{/**Main Content */}
<View style={[styles.content, { backgroundColor: colors.primary }]}>
<View style={{ flex: 1 }}>
{/**For Header */}
<Header />
</View>
<View style={{ flex: 5 }}>
{/**For Content */}
<ScrollView style={{ flex: 1 }}>
<LoginContent />
</ScrollView>
</View>
<View style={{ flex: 1 }}>
{/**For Footer */}
<Footer />
</View>
</View>
</View>
</View>
);
}
function TabContent() {
console.log("tab_content rerendered");
return (
<View
style={{
flex: 1,
backgroundColor: colors.accent,
flexDirection: orientation.landscape ? "row" : "column",
}}>
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}></View>
<View style={{ flex: 1.5, justifyContent: "center" }}>
{/**Main Content */}
<View style={[styles.content, { backgroundColor: colors.primary }]}>
{/**Header Wrapper */}
<View style={{ justifyContent: "center" }}>
{/**Header Title */}
<Header />
</View>
{/**Content Wrapper */}
<LoginContent />
{/**Footer Wrapper */}
<View style={{ justifyContent: "center" }}>
{/** Login Button */}
<Footer />
</View>
</View>
</View>
</View>
);
}
function Header() {
console.log("header_component rerendered");
return (
<View style={{ margin: "5%" }}>
<Title>Welcome User</Title>
<Subheading>Please Sign In to Continue..</Subheading>
</View>
);
}
function Footer() {
console.log("footer_component rerendered");
return (
<View style={{ margin: isTablet ? "5%" : "3.5%" }}>
<Button
title="Login"
loading={false}
ViewComponent={LinearGradient}
containerStyle={{ maxWidth: isTablet ? "45%" : "100%" }}
buttonStyle={{ height: 50, borderRadius: 10 }}
linearGradientProps={{
colors: [colors.accent, colors.accentLight],
start: { x: 1, y: 1 },
end: { x: 1, y: 0 },
}}
onPress={() => {
VerifyInputs();
//navigation.navigate("verify");
}}
/>
</View>
);
}
function LoginContent() {
console.log("login_component rerendered");
return (
<View style={{ margin: "3%" }}>
{/**Login & Email Wrapper */}
<View style={{ flexDirection: isTablet ? "row" : "column" }}>
<View style={styles.input}>
<TextInput
mode="outlined"
label="Email"
value={login.email}
error={errorEmail}
theme={InputTheme}
onChangeText={(text) => setLogin({ ...login, email: text })}
/>
{errorEmail ? (
<HelperText visible={true} type="error" theme={{ colors: { error: "red" } }}>
{messageEmail}
</HelperText>
) : null}
</View>
<View style={styles.input}>
<View style={{ flexDirection: "row", alignItems: "center" }}>
<TextInput
mode="outlined"
label="Password"
value={login.password}
error={errorPWD}
theme={InputTheme}
secureTextEntry={secureEntry}
style={{ flex: 1, marginBottom: 5, marginEnd: isTablet ? 15 : 5 }}
onChangeText={(text) => setLogin({ ...login, password: text })}
/>
<Button
icon={
<Icon
name={secureEntry ? "eye-off-outline" : "eye-outline"}
size={30}
color={colors.primary}
/>
}
buttonStyle={{
width: 55,
aspectRatio: 1,
backgroundColor: colors.accent,
borderRadius: 10,
}}
containerStyle={{ marginStart: 5 }}
onPress={async () => setSecureEntry(!secureEntry)}
/>
</View>
{errorPWD ? (
<HelperText visible={true} type="error" theme={{ colors: { error: "red" } }}>
{messagePWD}
</HelperText>
) : null}
</View>
</View>
{/**License & Device Wrapper */}
<View style={{ flexDirection: isTablet ? "row" : "column" }}>
<View style={styles.input}>
<TextInput
mode="outlined"
label="License Key"
value={login.licenseKey}
error={errorLicense}
theme={InputTheme}
onChangeText={(text) => setLogin({ ...login, licenseKey: text })}
/>
{errorLicense ? (
<HelperText visible={true} type="error" theme={{ colors: { error: "red" } }}>
License Key cannot be Empty!
</HelperText>
) : null}
</View>
<View style={styles.input}>
<TextInput
mode="outlined"
label="Device Name"
value={login.deviceName}
error={errorDevice}
theme={InputTheme}
onChangeText={(text) => setLogin({ ...login, deviceName: text })}
/>
{errorDevice ? (
<HelperText visible={true} type="error" theme={{ colors: { error: "red" } }}>
Device Name cannot be empty
</HelperText>
) : null}
</View>
</View>
</View>
);
}
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle={"dark-content"} backgroundColor={colors.accent} />
{isTablet ? <TabContent /> : <MobileContent />}
</SafeAreaView>
);
}
As you can see by looking at the code.. my Login component's rendering responsive component layouts based on device type being phone or tablet, the following components MobileContent or TabContent.
MobileContent functional component wraps my main content in a ScrollView & TabContent functional component doesn't need a scrollview but has responsive scaling views.
Both of these parent components display my common components, Header,LoginContent & Footer.
My Header component just displays a standard title & subheading. My LoginContent component has all the TextInput fields in them with validation logic setup as well. My Footer component has the Submit/Login button. That's All.
So we can summarize the components tree for screen as:
Login Screen/Parent Component => Mobile Content/Tab Content => Header, LoginContent & Footer (Common Components)
Alright, so now what is the issue? the major issue for me lies for TextInputs in LoginContent component, but I assume the underlying issue is applicable for entire Login Screen component.
The ISSUE: When I click on TextInput fields, the input gets focus and Keyboard appears. Everything is fine till this point. As soon as I type even a single letter, single key press on keyboard.. The Keyboard closes immediately and the text input is lost focus.
Why it could be happening? I believe this might be the classic 'React Component Re-render' problem, which causes by TextInput to be re-rendered when state is updated by the onChangeText for the TextInput and hence it loses focus & that's why the Keyboard closes.
So, I hope to resolve atleast first issue from these two issues.
Issue 1: How do I prevent the Keyboard from constantly closing when I try to type something in any of the TextInput fields.
Issue 2: How can I better optimize my Parent & Children components rendering on every state update using the any of these two React Hooks useCallback() and useMemo()?
I've read the documentation for these two hooks useCallback & useMemo multiple times and still haven't grasped the concept of these hooks. All posted examples deal with a Counter Exmaple optimized using useCallback or useMemo but I'm not working with Counters here in my Screen.
I need a more practical exmaple of useCallback() & useMemo() in screen components. Perhaps an implementation of these two hooks in my current Login Screen component will help me understand & grasp the concept better.
Like I mentioned, solving Issue 1 is my highest priority atm, but I know a solution for Issue 2 will help me in the long run as well. It will be very helpful if you can resolve both of my issues here. Thanks for helping.
i got a Questions because I am new in React native! I was trying to find an Answer that fits with my Code. But nothing is working well! Im overloaded. Maybe some of u guys can help me with my problem!
So i need a "simple" confirming password message if someone is writing the two fields wrong because there is no match!
import React, { Component } from 'react';
import {View, Image, TextInput} from 'react-native';
import { Container, Header, Content, Button, Icon, Body, Left, Right,
Text, Title, Card, CardItem } from 'native-base';
import styles from '../Style/Style.js'
import Foect from 'foect';
import IconNew from 'react-native-vector-icons/MaterialIcons';
export default class AnmeldeScreen extends Component {
constructor(props) {
super(props);
this.state = {
icEye: 'visibility-off',
password: true,
}
}
static navigationOptions = {
header: null
}
changePwdType = () => {
let newState;
if (this.state.password) {
newState = {
icEye: 'visibility',
password: false
}
} else {
newState = {
icEye: 'visibility-off',
password: true
}
}
// set new state value
this.setState(newState)
};
render () {
var {navigate} = this.props.navigation;
return (
<Container>
{/*---------------------------------------------------- H E A D E R ------------------------------------------------------------*/}
<Header>
<Left>
<Button transparent onPress={() => navigate("Start", {})}>
<Icon name="arrow-back" />
</Button>
</Left>
<Body>
<Title style={styles.titelintervall}>Registrieren</Title>
</Body>
<Right>
</Right>
</Header>
{/*---------------------------------------------------- C O N T E N T ------------------------------------------------------------*/}
<Content padder>
<Image source={require('../images/low-carb-camp-logo-breit.png')} style={styles.secondimagelogo} />
<Foect.Form
defaultValue={{
email: ''
}}
onValidSubmit={model => {
console.log(model);
}}
>
{ /* you can use form for triggering submit or checking form state(form.isSubmitted, form.isValid, ...) */ }
{ form => (
<View>
{ /* every Foect.Control must have a name and optionally validation rules */ }
<Foect.Control name="fullName" required minLength={2} maxLength={32}>
{ /* you can use control for getting/setting it's value, checking/updating(control.isValid, control.markAsTouched(), ...) it's state, checking it's errors(control.errors.required) */ }
{ control => (
<View>
<Text style={{ marginTop: 10, color: '#8BC051' }}>Dein Name</Text>
<TextInput
style={{height: 40, borderColor: 'gray', borderBottomWidth: 1}}
/* mark control as touched on blur */
onBlur={control.markAsTouched}
/* update control's value */
onChangeText={(text) => control.onChange(text)}
/* get control's value */
value={control.value}
placeholder="Hier vollständigen Namen eingeben"
/>
{ /* check control state and show error if necessary */ }
{ control.isTouched &&
control.isInvalid &&
<Text style={{ color: 'red' }}>Bitte trage deinen Namen ein.</Text> }
</View>
) }
</Foect.Control>
<Foect.Control name="email" required email>
{ control => (
<View>
<Text style={{ marginTop: 10, color: '#8BC051' }}>Email</Text>
<TextInput
style={{height: 40, borderColor: 'gray', borderBottomWidth: 1}}
keyboardType="email-address"
onBlur={control.markAsTouched}
onChangeText={(text) => control.onChange(text)}
value={control.value}
placeholder="Hier E-Mail eingeben"
/>
{ control.isTouched &&
control.isInvalid &&
<View>
<Text style={{ color: 'red'}}>{control.value} ist keine gültige E-Mail</Text>
</View> }
</View>
) }
</Foect.Control>
<Foect.Control name="password" required pattern={/(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/}>
{ control => (
<View>
<Text style={{ marginTop: 10, color: '#8BC051' }}>Passwort
</Text>
<TextInput
style={{height: 40, borderColor: 'gray', borderBottomWidth: 1}}
secureTextEntry={true}
onBlur={control.markAsTouched}
onChangeText={(text) => control.onChange(text)}
value={control.value}
secureTextEntry={this.state.password}
placeholder="Passwort erstellen"
/>
<IconNew style={styles.icon}
name={this.state.icEye}
size={25}
color={this.props.iconColor}
onPress={this.changePwdType}
/>
{ control.isTouched &&
control.isInvalid &&
<View>
{ control.errors.pattern ?
<Text style={{ color: 'red' }}>Dein Passwort muss mindestens 8 Zeichen lang sein und mindestens ein Sonderzeichen oder eine Zahl enthalten.</Text> :
<Text style={{ color: 'red' }}>Bitte trage dein Passwort ein.</Text> }
</View> }
</View>
) }
</Foect.Control>
<Foect.Control name="confirmpassword">
{ control => (
<View>
<Text style={{ marginTop: 10, color: '#8BC051' }}>Passwort wiederholen
</Text>
<TextInput
style={{height: 40, borderColor: 'gray', borderBottomWidth: 1}}
secureTextEntry={true}
onBlur={control.markAsTouched}
onChangeText={(text) => control.onChange(text)}
value={control.value}
placeholder="Passwort bitte wiederholen"
/>
</View>
) }
</Foect.Control>
</View>
) }
</Foect.Form>
<View style={{marginTop: 10}}>
<Button full warning style={styles.buttonintervall} onPress={
() => navigate("Ziel", {})}>>>
<Text>Registrieren</Text>
</Button>
</View>
<Text style={styles.secondtextfb}> oder </Text>
<Card>
<CardItem>
<Icon name="logo-facebook" style={{color: '#234c6e'}} />
<Text>Über Facebook registrieren</Text>
<Right>
<Icon name="arrow-forward" />
</Right>
</CardItem>
</Card>
</Content>
</Container>
);
}
}