Undefined is not an object(evaluating '_this2.state') [duplicate] - javascript

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Are 'Arrow Functions' and 'Functions' equivalent / interchangeable?
(4 answers)
Closed 1 year ago.
I´m trying to render a form but I have some issues in the function ´getQuestions´.
The problem is with the colors, I can´t defined colors to the styles. (eg color:this.state.theme.someColor) I got an error ( Undefined is not an object(evaluating '_this2.state')
Instead I have to defined a variable and then passed to the color or backgroundColor. eg var fontColor = this.state.theme.someColor ant then in the return color:fontColor (psd: the others styles are working pretty well).
Also in the onPress in the Radio input I can´t call functions or the state for the same reason. What I am doing wrong? Thanks in advance for your help
The code:
import React, { Component } from 'react';
import {
Text,
StyleSheet,
ScrollView,
View,
ActivityIndicator
} from 'react-native';
import AsyncStorage from '#react-native-async-storage/async-storage';
import WebService from '../WebService/WebService';
import { decode } from 'html-entities';
import { NativeBaseProvider, Box, Radio, Checkbox, HStack } from 'native-base';
import { widthPercentageToDP as wp, heightPercentageToDP as hp } from 'react-native-responsive-screen';
import AwesomeButtonRick from "react-native-really-awesome-button/src/themes/rick";
class QuizComponent extends Component {
constructor(props) {
super(props);
const params = this.props.route.params;
this.webservice = new WebService();
this.state = {
load: true,
item: params.item,
questions: params.item.quiz[0].questions,
userAnswers: [],
theme: {
primary: "#FFFFFF",
font_color: "#000000",
secondary: "#FFFFFF",
contrast: "#CCCCCC"
}
}
}
componentDidMount() {
this.getData();
}
renderLoading = () => {
return (
<View style={[styles.load, {
backgroundColor: "#cccccc"
}]}>
<ActivityIndicator size="large" color="#ffffff" />
</View>
);
}
getData = async () => {
this.setState({ load: true })
try {
const user = await AsyncStorage.getItem('CurrentUser')
const platform = await AsyncStorage.getItem('Platform')
const userObject = JSON.parse(user)
const platformObject = JSON.parse(platform)
if (user) {
this.setState({
host: platformObject.host,
plataforma: platformObject.name,
user: userObject,
username: userObject.username,
user_id: userObject.userId,
api_key: userObject.apiKey,
theme: {
primary: platformObject.theme.primary,
font_color: platformObject.theme.accent,
secondary: platformObject.theme.secondary,
contrast: platformObject.theme.extra
}
})
setTimeout(() => {
this.setState({ load: false })
}, 500)
} else {
}
} catch (e) {
console.error(e)
}
}
getQuestions = () => {
var secondary = this.state.theme.secondary
var font_color = this.state.theme.font_color
var contrast = this.state.theme.contrast
console.log(this.state.userAnswers)
return (
<ScrollView style={[{
width: '100%',
maxHeight: '100%'
}]}>
{
this.state.questions.map(function (qIndex, index) {
switch (qIndex.type) {
case '1':
return (
<Box key={index} style={[styles.container_box]}>
<Text style={[styles.question], {
backgroundColor: this.state.theme.secondary,
color: font_color,
padding: '2%',
borderRadius: 5
}}>{decode(qIndex.question)}</Text>
<Box style={[{ marginTop: '2%' }]}>
<HStack>
<Radio.Group>
{qIndex.answers.map(function (ansIndex, index1) {
return (
<Radio
key={index + '-' + index1}
value={ansIndex.id}
onPress={() => {
console.log('press')
}}
>
<Text style={styles.answer, {
color: font_color,
marginBottom: '2%'
}}>
{' ' + decode(ansIndex.answer.replace('<p>', '').replace('</p>', ''))}
</Text>
</Radio>
)
})}
</Radio.Group>
</HStack>
</Box>
</Box>
)
break;
default:
return (
<View>Algo</View>
)
break;
}
})
}
<AwesomeButtonRick
style={[{
alignSelf: "center",
marginTop: '10%',
marginBottom: '20%'
}]}
backgroundColor={secondary}
backgroundDarker={contrast}
textColor={font_color}
paddingHorizontal={70}
textSize={18}
onPress={() => { console.log('hola') }}
>Terminar
</AwesomeButtonRick>
</ScrollView>
)
}
render() {
if (this.state.load === true) {
return(
<NativeBaseProvider>
{this.renderLoading()}
</NativeBaseProvider>
)
}else{
return (
<NativeBaseProvider>
<Box style={[styles.main_box], { backgroundColor: this.state.theme.primary,}}>
{this.getQuestions()}
</Box>
</NativeBaseProvider>
);
}
}
}
export default QuizComponent;

Related

Calling useNavigation hook from TouchableOpacity inside a DrawerNavigator from react-navigation pkg throwing error, full source on question body

I'm learning react and react-native last 4 days because need build an mobile app and having the following problem when coding a react-native app:
After some research I found a way to embed a TouchableOpacity inside the top bar of DrawerNavigator, just after the hamburger button and it works giving me an alert.
The problem comes when I try to replace the alert with a call to useNavigation().navigate('Login'); from inside it onPress attribute, it throw the error below.
I want mirror the behavior of menu item on the TouchableOpacity doing the navigation.
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem., js engine: hermes
info Reloading app...
The full source follow as:
import * as React from 'react';
import { Button, Image, StyleSheet, Text, TouchableOpacity, View, Alert } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import {
createDrawerNavigator,
DrawerContentScrollView,
DrawerItemList,
} from '#react-navigation/drawer';
// https://reactnavigation.org/docs/use-navigation/
import { useNavigation } from '#react-navigation/native';
const getScreenCtx = content => {
return (<View style={stl.boxCtx}>{content}</View>);
}
const Home = ({ navigation }) => {
return ( getScreenCtx(<Text style={stl.boxDescr}>Home</Text>) );
}
const Login = ({ navigation }) => {
return ( getScreenCtx(<Text style={stl.boxDescr}>Login</Text>) );
}
const Logout = ({ navigation }) => {
return ( getScreenCtx(<Text style={stl.boxDescr}>Logout</Text>) );
}
const navToLogin = () => {
useNavigation().navigate('Login');
}
const NestedTabBar = props => {
return (
<>
<TouchableOpacity
style={stl.itemNav}
onPress={
() => {
//Alert.alert('NavigateToLogin');
navToLogin();
}
} >
<Text>[NavigateToLogin]</Text>
</TouchableOpacity>
</>
);
}
const ContentTopWideHamburgBar = props => {
return <NestedTabBar />;// <Text style={stl.hamburgBar}>ContentTopHamburgBar</Text>
}
const ContentColapsibleSideBarMenuHeader = props => {
return <Text style={stl.sideMenuHeader}>SideBarMenuHeader</Text>
}
const ContentColapsibleSideBarMenuFooter = props => {
return <Text style={stl.sideMenuFooter}>SideBarMenuFooter</Text>
}
const ContentColapsibleSideBarMenu = props => {
return (
<View style={stl.sideBarMenu}>
<DrawerContentScrollView {...props}>
<ContentColapsibleSideBarMenuHeader/>
<DrawerItemList {...props} />
</DrawerContentScrollView>
<ContentColapsibleSideBarMenuFooter/>
</View>
);
}
const Drawer = createDrawerNavigator();
const ContentItensNavigationRouteMap = () => {
return (
<>
<Drawer.Screen component={Home} name='Home' />
<Drawer.Screen component={Login} name='Login' />
<Drawer.Screen component={Logout} name='Logout' />
</>
);
}
const DrawerNavigator = () => {
return (
<Drawer.Navigator
screenOptions={
{
headerStyle: { backgroundColor: 'magenta', },
headerTintColor: 'white', // hamburg color
headerTitleStyle: { fontWeight: 'bold', },
headerTitle: (props) => <ContentTopWideHamburgBar {...props} />
}
}
contentOptions={
{
activeTintColor: 'red',
activeBackgroundColor: 'red',
inactiveTintColor: 'red',
inactiveBackgroundColor: 'red',
}
}
drawerContent={props => <ContentColapsibleSideBarMenu {...props} />} >
{ContentItensNavigationRouteMap()}
</Drawer.Navigator>
);
};
export default function ShellNavigator() {
return (
<NavigationContainer>
<DrawerNavigator />
</NavigationContainer>
);
}
const stl = StyleSheet.create({
boxDescr: {
fontSize: 30,
margin: 10,
padding:10,
backgroundColor: 'lightblue',
color: 'red',
},
boxCtx:{
display: 'flex',
flex: 1,
fontSize: 30,
margin: 0,
backgroundColor: 'yellow',
},
hamburgBar: {
fontSize: 20,
margin: 10,
backgroundColor: 'pink',
},
sideMenuHeader: {
fontSize: 20,
margin: 10,
backgroundColor: 'blueviolet',
},
sideMenuFooter: {
fontSize: 20,
margin: 10,
backgroundColor: 'purple',
},
sideBarMenu: {
flex: 1,
fontSize: 20,
margin: 10,
backgroundColor: 'greenyellow',
},
itemNav: {
fontSize: 40,
padding: 10,
backgroundColor: 'red',
},
});
Here navToLogin is a stand alone function, not in a Functional component. Hooks need to be in a functional component to use. The bellow code should work which navToLogin move the function inside a functional component.
const NestedTabBar = props => {
const navigation = useNavigation();
const navToLogin = () => {
navigation.navigate('Login');
}
return (
<>
<TouchableOpacity
style={stl.itemNav}
onPress={
() => {
//Alert.alert('NavigateToLogin');
navToLogin();
}
} >
<Text>[NavigateToLogin]</Text>
</TouchableOpacity>
</>
);
}

React native `input` must be a function received undefined

I have an array which displays a list of jobs with the radio button(each).If I click any radio button I want to get its value so far I'm getting an error input must be a function received undefined. how can I achieve something like this in react native?
Code
import React, {useState} from 'react';
import {View, Text, TextInput, Image,Button,Body,Pressable,ScrollView,Switch} from 'react-native';
export default function Home({ navigation}){
let roles = navigation.getParam('roles')
const [isEnabled, setIsEnabled] = useState(false);
const toggleSwitch = () => setIsEnabled(previousState => !previousState);
const handleClick = (data, e) => {
alert(e.target.value, data);
}
<View>
{
Object.keys(roles).map((key) => {
return (
<View>
<View style={styles.XYQHC}>
<Text>{roles[key]['name']}</Text>
<Switch
trackColor={{ false: "#767577", true: "#81b0ff" }}
thumbColor={isEnabled ? "#f5dd4b" : "#f4f3f4"}
ios_backgroundColor="#3e3e3e"
onValueChange={toggleSwitch}
value={isEnabled}
/>
<input type="radio" name="roles" value={roles[key]['name']} defaultChecked={roles[key]['id'] == 3 ? true : false } onClick={handleClick.bind(this, roles[key]['name'])}/>
</View>
</View>
)
})
}
</View>
};
I had to edit a little bit to make it work and to assume some input data but here is a working example of your code:
import React, { useState, useEffect } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
function RadioButton({ label, onPress, selected, style }) {
return (
<TouchableOpacity
style={[
{
flexDirection: 'row',
alignItems: 'center',
marginLeft: 12,
marginVertical: 20,
},
style,
]}
onPress={onPress}>
<View
style={{
height: 24,
width: 24,
borderRadius: 12,
borderWidth: 2,
borderColor: '#000',
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
}}>
{selected ? (
<View
style={{
height: 12,
width: 12,
borderRadius: 6,
backgroundColor: '#000',
}}
/>
) : null}
</View>
<Text>{label}</Text>
</TouchableOpacity>
);
}
export default function Home({ navigation }) {
// let status = navigation.getParam('status');
let status = 2;
// let roles = navigation.getParam('roles');
let roles = [
{
id: 1,
name: 'lawyer',
},
{
id: 2,
name: 'farmer',
},
{
id: 3,
name: 'architect',
},
];
const [isEnabled, setIsEnabled] = useState(false);
const [chosenRole, setChosenRole] = useState(false);
useEffect(() => {
roles.find((role) => {
if (role.id === status) {
setChosenRole(role);
return true;
}
return false;
});
}, []);
const handleClick = (data) => {
console.log(data);
setChosenRole(data);
};
return (
<View style={{ marginTop: 30 }}>
{roles.map((role) => {
return (
<View>
<RadioButton
label={role.name}
selected={role.name === chosenRole.name}
onPress={() => handleClick(role)}
/>
</View>
);
})}
</View>
);
}

Invariant Violation Text string must be rendered within a <Text> component

I'm really sorry to bother you, but I try to learn React-Native. I'm trying to make a weather app and I have this error :
Text strings must be rendered within a <Text> component.
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:4137:8 in <anonymous>
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:4134:2 in createTextInstance
- ... 9 more stack frames from framework internal
Here's are my two files of code:
weather-card.js
import React, {Component} from "react";
import {Animated, View, Text, PanResponder, Image} from "react-native";
import {
widthPercentageToDP as wp,
heightPercentageToDP as hp
} from "react-native-responsive-screen";
const CARD_INITIAL_POSITION_Y = hp("80%");
const CARD_INITIAL_POSITION_X = wp("5%");
const TRESHOLD_TO_TOP = hp("75%");
const TRESHOLD_TO_BOTTOM = hp("70%");
const CARD_OPEN_POSITION = hp("45%");
const MAX_DRAG_ZONE_WHEN_OPEN = hp("65%");
const ICON_URL = "http://openweathermap.org/img/w/";
class WeatherCard extends Component {
state = { panResponder : undefined, isOpen: false};
componentDidMount() {
this.position = new Animated.ValueXY();
this.position.setValue({x:CARD_INITIAL_POSITION_X, y: CARD_INITIAL_POSITION_Y});
const panResponder = PanResponder.create({
onStartShouldSetPanResponder:() => true,
onPanResponderMove:(e,gesture) => {
if(!(this.state.isOpen && gesture.y0 > MAX_DRAG_ZONE_WHEN_OPEN)) {
this.position.setValue({
x:CARD_INITIAL_POSITION_X,
y: gesture.moveY
});
}
},
onPanResponderRelease:(e, gesture) => {
if (!this.state.isOpen){
if(gesture.moveY <= TRESHOLD_TO_TOP) {
this.setOpenPosition(() => this.setState({isOpen: true}));
} else {
this.resetPosition();
}
} else {
if(gesture.moveY <= TRESHOLD_TO_BOTTOM){
this.setOpenPosition()
} else {
if(gesture.y0 < MAX_DRAG_ZONE_WHEN_OPEN){
this.resetPosition(() => this.setState({isOpen: false}))
}
}
}
}
});
this.setState({panResponder})
}
setOpenPosition = (done) => {
Animated.spring(this.position, {
toValue: {x: CARD_INITIAL_POSITION_X, y : CARD_OPEN_POSITION}
}).start(() => done && done());
};
resetPosition = (done) => {
Animated.spring(this.position, {
toValue: {x: CARD_INITIAL_POSITION_X, y : CARD_INITIAL_POSITION_Y}
}).start(() => done && done())
};
getCardStyle() {
return {
width: wp("90%"),
height: hp("110%"),
borderRadius: 10,
zIndex: 2,
backgroundColor: "white",
elevation: 1,
shadowColor: "black",
shadowOpacity: 0.2,
shadowOffset: { height: 2, width: 2 },
position: "absolute",
left: CARD_INITIAL_POSITION_X,
padding: hp("2%"),
...this.position.getLayout()
};
}
renderHeader(){
return (
<View
style={{
justifyContent: "center",
alignItems: "center"
}}
>
<Text style={{ fontSize: 30, marginBottom: hp("1%") }}>
{this.props.currentWeather.name}
</Text>
<View style={{ flexDirection: "row" }}>
<Text style={{ marginTop: hp("1%"), fontSize: 35 }}>
{this.props.currentWeather.main.temp}
</Text>
<Image
style={{ height: 60, width: 60 }}
source={{
uri: `${ICON_URL}${this.props.currentWeather.weather[0].icon}.png`
}}
/>
</View>
</View>
)
}
render() {
return this.state.panResponder ? <Animated.View {...this.state.panResponder.panHandlers} style={this.getCardStyle()}> {this.renderHeader()} </Animated.View> : <View />;
}
}
export default WeatherCard;
And search-screen.js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import MapView from 'react-native-maps';
import {SearchBar} from "react-native-elements";
import {
widthPercentageToDP as wp,
heightPercentageToDP as hp
} from "react-native-responsive-screen";
import {connect} from "react-redux";
import {getCurrentWeatherByCity} from "../actions/index";
import WeatherCard from "../components/weather-card";
const DEFAULT_COORD = {
lat: 48.859268,
lng: 2.347060
};
class SearchScreen extends React.Component {
state={search: ""};
updateSearch = search => {
this.setState({search})
};
submitSearch = () => {
this.props.getCurrentWeatherByCity(this.state.search);
console.log(this.state.search)
};
render() {
console.log(this.props.currentWeather);
return (
<View style={styles.container}>
<MapView style={{flex : 1}}
region={{latitude : this.props.currentWeather ? this.props.currentWeather.coord.lat : DEFAULT_COORD.lat, longitude : this.props.currentWeather ? this.props.currentWeather.coord.lon : DEFAULT_COORD.lng, latitudeDelta: 0.2000, longitudeDelta: 0.1000}}
scrollEnabled={false}
liteMode={true} />
{this.props.currentWeather && <WeatherCard currentWeather={this.props.currentWeather} />}
<SearchBar
lightTheme
onChangeText={this.updateSearch}
value={this.state.search}
onSubmitEditing={this.submitSearch}
containerStyle={{
position: "absolute",
bottom: hp("50%"),
left: wp("5%"),
width: wp("90%")
}}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
},
});
const mapStateToProps = (store) => {
return {
currentWeather : store.weather.currentWeather
}
};
const mapDispatchToProps = {
getCurrentWeatherByCity
};
export default connect(mapStateToProps, mapDispatchToProps)(SearchScreen)
I was asking myself if the problem could be :
{this.props.currentWeather && <WeatherCard currentWeather={this.props.currentWeather} />}
Because it's not inside a Text component...
If you can help, it could be really great :D
If you need other resources, do not hesitate!
Thanks in advance :)
It might be because of this try this:
change
{this.props.currentWeather.name}
to
{this.props.currentWeather.name != undefined ? this.props.currentWeather.name : null}
Similarly do the above thing for {this.props.currentWeather.main.temp}
Hope this helps!
I would like to post my answer if someone has the same problem. The error says that a text string is "outside" a component. However it wasn't the case. It was indentation's problem. So don't forget to pay attention about that :D Expo can be mean !

ReactJS TypeError: this.props. is not a function

I'm building an application, where there is a form presented with different steps. In all the steps but one, I manage to provide the necessary functions as props to make some operations such as 'handleNext', 'handleBack' or 'handleChange'.
Nevertheless, in the last step, represented in the class SuccessForm, when I try to execute the 'handleDownload' function, I get the following error:
TypeError: this.props.handleDownload is not a function
Here it is the SuccessForm.js class:
export class SuccessForm extends Component {
constructor() {
super();
}
download = e => {
e.preventDefault();
this.props.handleDownload();
}
render() {
return (
<React.Fragment>
<Grid container>
<Grid item xs={12} sm={2}>
<DirectionsWalkIcon fontSize="large" style={{
fill: "orange", width: 65,
height: 65
}} />
</Grid>
<Grid>
<Grid item xs={12} sm={6}>
<Typography variant="h5" gutterBottom>
Route created
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="subtitle1">
Your new track was succesfully created and saved
</Typography>
</Grid>
</Grid>
<Tooltip title="Download" arrow>
<IconButton
variant="contained"
color="primary"
style={{
marginLeft: 'auto',
// marginRight: '2vh'
}}
onClick={this.download}
>
<GetAppIcon fontSize="large" style={{ fill: "orange" }} />
</IconButton>
</Tooltip>
</Grid>
</React.Fragment>
)
}
}
The entire NewRouteForm.js:
import React, { Component } from 'react'
import { makeStyles, MuiThemeProvider } from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
import Stepper from '#material-ui/core/Stepper';
import Step from '#material-ui/core/Step';
import StepLabel from '#material-ui/core/StepLabel';
import Button from '#material-ui/core/Button';
import Typography from '#material-ui/core/Typography';
import DataForm from '../stepper/dataform/DataForm';
import ReviewForm from '../stepper/reviewform/ReviewForm';
import MapForm from '../stepper/mapform/MapForm';
import NavBar from '../../graphic interface/NavBar';
import DirectionsWalkIcon from '#material-ui/icons/DirectionsWalk';
import Avatar from '#material-ui/core/Avatar';
import CheckCircleOutlineOutlinedIcon from '#material- ui/icons/CheckCircleOutlineOutlined';
import FilterHdrIcon from '#material-ui/icons/FilterHdr';
import Grid from '#material-ui/core/Grid';
import SuccessForm from '../stepper/success/SuccessForm';
import { withStyles } from '#material-ui/styles';
export class NewRouteForm extends Component {
state = {
activeStep: 0,
name: '',
description: '',
date: new Date(),
photos: [],
videos: [],
points: []
};
handleNext = () => {
const { activeStep } = this.state;
this.setState({ activeStep: activeStep + 1 });
};
handleBack = () => {
const { activeStep } = this.state;
this.setState({ activeStep: activeStep - 1 });
};
handleChange = input => e => {
this.setState({ [input]: e.target.value });
}
handleDateChange = date => {
this.setState({ date: date });
}
handleMediaChange = (selectorFiles: FileList, code) => { // this is not an error, is TypeScript
switch (code) {
case 0: // photos
this.setState({ photos: selectorFiles });
break;
case 1: // videos
this.setState({ videos: selectorFiles });
break;
default:
alert('Invalid media code!!');
console.log(code)
break;
}
}
handleMapPoints = points => {
this.setState({ points: points })
}
// ###########################
// Download and Upload methods
// ###########################
handleDownload = () => {
// download route
console.log("DOWNLOAD")
alert("DOWNLOAD");
}
upload = () => {
// upload route
}
render() {
const { activeStep } = this.state;
const { name, description, date, photos, videos, points } = this.state;
const values = { activeStep, name, description, date, photos, videos, points };
const { classes } = this.props;
return (
<MuiThemeProvider>
<React.Fragment>
<NavBar />
<main className={classes.layout}>
<Paper className={classes.paper}>
<Avatar className={classes.avatar}>
<FilterHdrIcon fontSize="large" />
</Avatar>
<Typography component="h1" variant="h4" align="center">
Create your own route
</Typography>
<Stepper activeStep={activeStep} className={classes.stepper}>
{steps.map((label) => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
</Step>
))}
</Stepper>
<React.Fragment>
{activeStep === steps.length ? (
<SuccessForm />
) : (
<React.Fragment>
{getStepContent(activeStep,
values,
this.handleNext,
this.handleBack,
this.handleChange,
this.handleDateChange,
this.handleMediaChange,
this.handleMapPoints,
this.handleDownload
)}
</React.Fragment>
)}
</React.Fragment>
</Paper>
</main>
</React.Fragment>
</MuiThemeProvider>
)
}
}
const steps = ['Basic data', 'Map', 'Review your route'];
function getStepContent(step,
values,
handleNext,
handleBack,
handleChange,
handleDateChange,
handleMediaChange,
handleMapPoints,
handleDownload) {
switch (step) {
case 0:
return <DataForm
handleNext={handleNext}
handleChange={handleChange}
handleDateChange={handleDateChange}
handleMediaChange={handleMediaChange}
values={values}
/>;
case 1:
return <MapForm
handleNext={handleNext}
handleBack={handleBack}
handleMapPoints={handleMapPoints}
values={values}
/>;
case 2:
return <ReviewForm
handleNext={handleNext}
handleBack={handleBack}
values={values}
/>;
case 3:
return <SuccessForm
handleDownload={handleDownload}
/>;
default:
throw new Error('Unknown step');
}
}
const useStyles = theme => ({
layout: {
width: 'auto',
marginLeft: theme.spacing(2),
marginRight: theme.spacing(2),
[theme.breakpoints.up(600 + theme.spacing(2) * 2)]: {
width: 600,
marginLeft: 'auto',
marginRight: 'auto',
},
},
paper: {
marginTop: theme.spacing(3),
marginBottom: theme.spacing(3),
padding: theme.spacing(2),
[theme.breakpoints.up(600 + theme.spacing(3) * 2)]: {
marginTop: theme.spacing(6),
marginBottom: theme.spacing(6),
padding: theme.spacing(3),
},
},
stepper: {
padding: theme.spacing(3, 0, 5),
},
buttons: {
display: 'flex',
justifyContent: 'flex-end',
},
button: {
marginTop: theme.spacing(3),
marginLeft: theme.spacing(1),
},
avatar: {
marginLeft: 'auto',
marginRight: 'auto',
backgroundColor: theme.palette.warning.main,
},
icon: {
width: 65,
height: 65,
},
grid: {
marginLeft: theme.spacing(2),
}
});
export default withStyles(useStyles)(NewRouteForm);
Try calling super(props) in the constructor:
constructor(props) {
super(props);
}
and passing function with this instance (this.handleDownload) as it is a class property:
<SuccessForm handleDownload={this.handleDownload} />
Update:
You have a bug on the last step when you not passing a property:
activeStep === steps.length ? <SuccessForm handleDownload={this.handleDownload}/>
Assuming that you have a class in your parent Component, what you're missing is the this keyword in the function reference...
case 3:
return <SuccessForm
handleDownload={this.handleDownload}
/>;

React Native Redux causing whole page to render upon calling function

I have an issue with React Native Redux where I am calling a Redux action from the onChangeText function from inside a TextField. Normally, an update like this would only cause the TextField itself to update, however the whole page is being re-rendered upon each call of the function.
Here is the code for the TextField. The method in question is this.props.updateURL(value)
<TextField label='Server'
value={this.props.profile.url}
labelFontSize={textFont(16)}
tintColor='#B3B2B6'
autoCapitalize="none"
autoCorrect={false}
returnKeyType={'next'}
blurOnSubmit={false}
autoFocus={!this.props.profile.edited}
style={styles.inputStyle}
selectionColor={CUSRSOR_COLOR}
editable={!this.props.profile.edited}
onChangeText={async value => {
this.props.updateURL(value)
}}
renderAccessory={()=>{
return(<TouchableOpacity onPress={this.handleScanPress} >
<Image source={ImgScan} style={{height: 25, width: 25, overflow:'visible', marginBottom:20, marginRight: 10}}/>
</TouchableOpacity>);
}}
>
</TextField>
This is for the Redux methods mapStateToProps and mapDispatchToProps:
const mapStateToProps = (state) => {
return {
profile : state.profileReducer,
deviceUid: state.GlobalReducer.deviceUid,
locationsList: state.LogsReducer.locationList,
errorList: state.ErrorsReducer.failedList,
watermarkText: state.GlobalReducer.watermarkText,
alertPresent: state.LogsReducer.alertPresent
}
};
const mapDispatchToProps = (dispatch) => {
return {
updateURL : (url) => dispatch(updateURL(url)),
updateUserName : (userName) => dispatch(updateUserName(userName)),
updatePassword : (password) => dispatch(updatePassword(password)),
updateDeviceName : (deviceName) => dispatch(updateDeviceName(deviceName)),
allFieldsEntered : (url,userName,password) => dispatch(allFieldsEntered(url,userName,password)),
loginDevice: (url,userName,password,deviceName,deviceId,navigation,existingUser) => dispatch(callLoginApi(url,userName,password,deviceName,deviceId,navigation,existingUser)),
updateFailureMessage: (message) => dispatch(updateFailureMessage(message)),
showLoader: () => dispatch(showLoader()),
hideLoader: () => dispatch(hideLoader()),
updateRegistrationSuccess: (value) => dispatch(updateRegistrationSuccess(value)),
checkForInternet: () => dispatch(checkConnectivity()),
getProfileDetails: async(isToken) => await dispatch(fetchProfileDetails(isToken)),
updateLocationSwitch: (value) => dispatch(updateLocationStatus(value)),
updateIsEdit: (value) => dispatch(updateIsEdit(value)),
storeRegistrationFlagInAsync: () => dispatch(storeRegistrationFlagInAsync()),
isDeviceConnected: (value) => dispatch(isDeviceConnected(value)),
requestLocationPermissions: () => dispatch(requestLocationPermissions()),
passwordChanged: (value) => dispatch(updateIsPasswordChanged(value)),
isEditPage: (value) => dispatch(isEditPage(value)),
getDeviceName: () => dispatch(getDeviceName()),
shouldEnableLocation: (value) => dispatch(shouldEnableLocation(value)),
fetchLocationList: () => dispatch(fetchLocations()),
syncRecords: (url,userName,password,locationList,deviceId,isFromError,shouldPresentErrorMsg) => dispatch(syncFailedRecords(url,userName,password,locationList,deviceId,isFromError,shouldPresentErrorMsg)),
fetchFailedRecords: (shouldShowLoader) => dispatch(fetchFailedRecords(shouldShowLoader)),
updateDeviceAction: (action,deviceId,url) => dispatch(performDeviceAction(action,deviceId,url)),
callLogoutApi: (userName,password) => dispatch(callLogoutApi(userName,password)),
syncDeviceActions: () => dispatch(syncDeviceActions()),
syncPendingUpdates: (url,userName,password,deviceId) => dispatch(syncPendingUpdates(url,userName,password,deviceId)),
fetchPendingLocationsList: () => dispatch(fetchPendingLocationsList()),
showPasswordChangeAlert: (alertFlag,navigation) => dispatch(handleUIForPasswordChange(alertFlag,navigation)),
}
}
export default connect(mapStateToProps,mapDispatchToProps)(Profile);
Here are the reducers:
const initialState = {
url: '',
userName : '',
password : '',
deviceName : '',
resourceCode : '',
isDeviceConnected : false,
allFieldsFilled: false,
token: '',
gpsTrackingInterval : 5,
apiPostingInterval : 30,
failureMessage : '',
registered : false,
edited: false,
editPage: false,
isConnected: true,
shouldEnableLocation: false,
resourceDesc: '',
adminEmail: '',
companyName: ''
}
const profileReducer = (state=initialState,action) => {
switch (action.type) {
case actionTypes.UPDATE_URL :
return {
...state,
url: action.payload
}
...
}
This is the Redux action:
export const updateURL = (url) => {
return {
type: actionTypes.UPDATE_URL,
payload: url
};
};
Any help would be much appreciated.
import React, { Component } from 'react';
import {
StyleSheet, Text, View, TextInput, TouchableOpacity, Image,
KeyboardAvoidingView, ScrollView, SafeAreaView, Linking
} from 'react-native';
import { COLOR_BLACK, COLOR_GRAY_9, COLOR_TRANSPARENT, COLOR_GRAY_4 } from '../../../constants/colors';
import ImgProntoLogo from '../../../assets/images/pronto-logo.png';
import ImgPasswordVisibleIcon from '../../../assets/images/visibility.png';
import ImgUrl from '../../../assets/images/url.png';
import ImgBarcode from '../../../assets/images/scan-barcode.png';
import ImgUsernameIcon from '../../../assets/images/username.png';
import ImgPasswordIcon from '../../../assets/images/password.png';
import ImgPasswordHide from '../../../assets/images/password-hide.png';
import GlobalStyleSheet from '../../../constants/styles';
import {
PASSWORD, USERNAME, URL_CONNECT
} from '../../../constants/strings';
import { StackActions, NavigationActions } from 'react-navigation';
import Sync from '../../../utils/Syncing';
import { AsyncStorage } from 'react-native';
export default class Configuration extends Component {
constructor(props) {
super(props)
this.handleOpenURL = this.handleOpenURL.bind(this);
this.handleScanPress = this.handleScanPress.bind(this);
}
async componentWillMount() {
this.props.clearConfiguration();
this.props.checkConnectivity();
Linking.addEventListener('url', this.handleOpenURL);
Sync.removeDataFromUserTable();
Sync.removeDataFromCompanyTable();
}
componentDidMount() {
console.log("COMPONENT MOUNTING");
Linking.getInitialURL().then((url) => {
if (url) {
url = url.substring(18);
this.props.updateURL(url);
}
}).catch(err => console.error('An error occurred', err))
}
componentWillReceiveProps(props) {
console.log("WILL RECEIVE")
}
handleOpenURL = (event) => {
this.props.updateURL(event.url.substring(18))
}
copyRights() {
var currentYear = new Date().getFullYear().toString();
return '© '.concat(currentYear).concat(' Pronto Software Limited.All Rights Reserved.');
}
// Connect the device to the pronto connect
async connectDevice() {
this.props.showLoader();
this.props.resetConnectPasswordVisibility();
await this.props.connectDevice(this.props.configuration.url,
this.props.configuration.username, this.props.configuration.password);
let isDeviceConnected = await AsyncStorage.getItem("isConnected");
if (isDeviceConnected === "true") {
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'Register' })],
});
this.props.navigation.dispatch(resetAction);
}
this.props.hideLoader();
}
static navigationOptions = {
header: null
}
handleScanPress() {
const action = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'Scanner' })],
});
this.props.navigation.push('Scanner');
}
render() {
return (
<SafeAreaView style={GlobalStyleSheet.container}>
<KeyboardAvoidingView style={GlobalStyleSheet.container} >
<View style={styles.headerLogoView}>
<Image source={ImgProntoLogo} style={styles.logo} />
</View>
<ScrollView keyboardShouldPersistTaps="handled" style={styles.scrollViewStyle}>
<View style={{ flex: 1, height: '100%' }}>
<View style={styles.viewContainer}>
<Text style={[GlobalStyleSheet.textHeading4, GlobalStyleSheet.marginTop10,
GlobalStyleSheet.marginBottom10]}>CONFIGURE DEVICE</Text>
<View style={[GlobalStyleSheet.textInputView, GlobalStyleSheet.marginBottom10]}>
<Image style={GlobalStyleSheet.iconInputBar} source={ImgUrl} />
<TextInput
underlineColorAndroid={COLOR_TRANSPARENT}
style={GlobalStyleSheet.textInput}
autoCapitalize='none'
placeholder={URL_CONNECT}
value={this.props.configuration.url}
onChangeText={(value) => { this.props.updateURL(value) }}
editable={!(this.props.configuration.isDeviceConnected)}
/>
<TouchableOpacity style={[GlobalStyleSheet.passwordTouchable]} onPress={this.handleScanPress}>
<Image style={[GlobalStyleSheet.passwordShowHideIcon]} source={ImgBarcode} />
</TouchableOpacity>
</View>
<View style={[GlobalStyleSheet.textInputView, GlobalStyleSheet.marginBottom10]}>
<Image style={GlobalStyleSheet.iconInputBar} source={ImgUsernameIcon} />
<View style={[GlobalStyleSheet.flexRow, { flex: 1 }]}>
<TextInput
placeholder={USERNAME}
underlineColorAndroid={COLOR_TRANSPARENT}
style={GlobalStyleSheet.textInput}
onChangeText={(value) => { this.props.updateUsername(value) }}
editable={!(this.props.configuration.isDeviceConnected)}
/>
</View>
</View>
<View style={[GlobalStyleSheet.textInputView, GlobalStyleSheet.marginBottom10]} >
<Image style={[GlobalStyleSheet.iconInputBar]} source={ImgPasswordIcon} />
<View style={[GlobalStyleSheet.flexRow, { flex: 1 }]}>
<TextInput
placeholder={PASSWORD}
underlineColorAndroid={COLOR_TRANSPARENT}
contextMenuHidden={true}
style={GlobalStyleSheet.textInput}
autoCapitalize='none'
onChangeText={(value) => { this.props.updatePassword(value) }}
editable={!(this.props.configuration.isDeviceConnected)}
secureTextEntry={!(this.props.configuration.passwordVisibility)} />
<TouchableOpacity style={[GlobalStyleSheet.passwordTouchable]}
onPress={() => {
if (!this.props.configuration.isDeviceConnected) {
this.props.togglePasswordVisibility(this.props.configuration.passwordVisibility)
}
}}>
{
this.props.configuration.passwordVisibility ?
(<Image style={[GlobalStyleSheet.passwordShowHideIcon]} source={ImgPasswordVisibleIcon} />) :
(<Image style={[GlobalStyleSheet.passwordShowHideIcon]} source={ImgPasswordHide} />)
}
</TouchableOpacity>
</View>
</View>
{
this.props.configuration.isDeviceConnected ?
(
<TouchableOpacity style={GlobalStyleSheet.touchableBarProcessed} disabled={true} >
<Text style={GlobalStyleSheet.textTouchableBarProcessed}>CONNECTED</Text>
</TouchableOpacity>
) :
(this.props.configuration.url != "" &&
this.props.configuration.username != "" && this.props.configuration.password != "" ?
(
<TouchableOpacity style={[GlobalStyleSheet.touchableBarEnabled]}
onPress={async () => { await this.connectDevice() }}>
<Text style={GlobalStyleSheet.textTouchableBarEnabled}>CONNECT</Text>
</TouchableOpacity>
) :
(
<TouchableOpacity style={[GlobalStyleSheet.touchableBarDisabled]} disabled={true}>
<Text style={[GlobalStyleSheet.textTouchableBarDisabled]}>CONNECT</Text>
</TouchableOpacity>
)
)
}
</View>
</View>
<View style={styles.copyRightsView}>
<Text style={GlobalStyleSheet.copyRightsText}>{this.copyRights()}</Text>
</View>
</ScrollView>
</KeyboardAvoidingView >
</SafeAreaView >
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
height: '100%',
width: '100%',
backgroundColor: COLOR_GRAY_9,
},
headerLogoView: {
height: 50,
backgroundColor: COLOR_GRAY_9,
elevation: 5,
justifyContent: 'center',
alignItems: 'center',
marginBottom: 2,
shadowOffset: { width: 2, height: 2, },
shadowColor: COLOR_BLACK,
shadowOpacity: 0.1,
},
logo: {
height: 40,
width: '80%',
resizeMode: 'contain',
},
viewContainer: {
marginLeft: 15,
marginRight: 15,
paddingBottom: 10
},
copyRightsView: {
alignItems: 'center',
minHeight: 40,
paddingBottom: 5,
},
scrollViewStyle: {
flex: 1,
height: '100%',
paddingTop: 10
},
});

Categories