in the file popUpDialog.Js
export default class DialogTester extends Component {
constructor(props) {
super(props)
this.state = {
dialogVisible: false
};
}
showDialog = () => {
this.setState({ dialogVisible: true });
};
handleCancel = () => {
this.setState({ dialogVisible: false });
};
handleRedefinir = () => {
this.setState({ dialogVisible: false });
};
handleEmail = (email) => {
console.log(email);
}
render() {
const {dialogVisible} = this.state;
return (
<View>
<Dialog.Container visible={this.state.dialogVisible}>
<Dialog.Title>Redefinir Senha</Dialog.Title>
<Dialog.Description>
Digite seu e-mail cadastrado
</Dialog.Description>
<Dialog.Input placeholder="E-mail" onChangeText={(email) => this.handleEmail(email)}
></Dialog.Input>
<Dialog.Button label="Cancelar" onPress={this.handleCancel} />
<Dialog.Button label="Redefinir" onPress={this.handleRedefinir} />
</Dialog.Container>
</View>
);
}
}
so far all right
in the file Index.js
import React, { Component } from "react";
import {
View,
TextInput,
Text,
TouchableOpacity,
SafeAreaView,
StatusBar,
} from "react-native";
import styles from "./styles";
import PopUp from "../Login/popUpDialog";
export default class Login extends Component {
render() {
return (
<SafeAreaView>
<TouchableOpacity
onPress={() => <PopUp dialogVisible = true /> } //It does not work
style={styles.redefinirButton}
>
<Text style={styles.textRedefinirButton}>Redefinir Senha</Text>
</TouchableOpacity>
</SafeAreaView>
);
}
}
How do I make dialogVisible = true when I press it?
I try props and setState don't work
Everything else works, if I try to put out of onPress and leave the variable as true as default it shows, but when I leave it false and try to pass true when I press the button I can't do it in any way.
You need to keep the state of the visibility in the parent component (Login) and pass it as a prop to the Popup component. Then, in the Popup component just use props.dialogVisible where you need it
Related
I am trying to test this simple button:
import React, { FC, ReactNode } from 'react';
import { TouchableOpacity, GestureResponderEvent, ViewStyle } from 'react-native';
type Props = {
style: ViewStyle;
onPress: (event: GestureResponderEvent) => void;
disabled?: boolean;
activeOpacity?: number;
children: ReactNode;
};
export const Button: FC<Props> = ({
style,
onPress,
disabled,
activeOpacity,
children
}) => {
return (
<TouchableOpacity
activeOpacity={ activeOpacity }
onPress={ onPress }
style={ style }
disabled={ disabled }
testID={ 'button' }
>
{ children }
</TouchableOpacity>
);
};
I use this test file, where I simply render my Button with some props:
import React from 'react';
import { Text, StyleSheet } from 'react-native';
import { render } from '#testing-library/react-native';
import { ReactTestInstance } from 'react-test-renderer';
import { Button } from '../../../src/components/Button';
const styles = StyleSheet.create({
button: {
height: 50
}
});
const onPressMock = jest.fn();
describe('FilterForm', () => {
it('should render Button with default arguments', () => {
const { queryByText, debug } = render(
<Button style={ styles.button } onPress={ onPressMock } disabled activeOpacity={ 0.3 }>
<Text>{ 'Dummy Test Text' }</Text>
</Button>
);
debug();
// Not important - just in case you are curious //
let buttonText = queryByText('Dummy Test Text');
expect(buttonText).not.toBeNull();
buttonText = buttonText as ReactTestInstance;
expect(buttonText.parent?.parent?.props.testID).toEqual('button');
expect(buttonText.parent?.parent?.props.activeOpacity).toEqual(0.3);
expect(buttonText.parent?.parent?.props.disabled).toEqual(true);
});
});
The problem is that I get this tree returned, which does not have disabled or activeOpacity in it:
<View
accessible={true}
focusable={true}
onClick={[Function onClick]}
onResponderGrant={[Function onResponderGrant]}
onResponderMove={[Function onResponderMove]}
onResponderRelease={[Function onResponderRelease]}
onResponderTerminate={[Function onResponderTerminate]}
onResponderTerminationRequest={[Function onResponderTerminationRequest]}
onStartShouldSetResponder={[Function onStartShouldSetResponder]}
style={
Object {
"height": 50,
"opacity": 1,
}
}
testID="button"
>
<Text>
Dummy Test Text
</Text>
</View>
Because of that my assertions in the test file above fail. How can I test the props of TouchableOpacity then?
Thanks in advance for your time!
I can call disabled prop by using fireEvent(button, 'press'). Disabled button will not call the handler, so I can assert it with expect(handlerMock).not.toBeCalled().
As to activeOpacity, I guess storybook should be used for visual testing.
The function I am not able to run is the navigation functions in my example it's
this.this.props.navigation.goBack()
My Login File is posted below but the part with the problem is the short snippet
The error I am getting is: TypeError: undefined is not an object (evaluating 'LogIn.props.navigation')
First failed Snippet
static navigationOptions = {
headerLeft: () => (
<Button
onPress={()=>this.props.navigation.goBack()}
title="cancel"
color={colors.black}
/>
),
};
LogIn.js
import React, { Component } from 'react';
import { PropTypes } from 'prop-types';
import Icon from 'react-native-vector-icons/FontAwesome';
import colors from '../styles/colors';
import {
View,
Text,
ScrollView,
StyleSheet,
KeyboardAvoidingView,
Button
} from 'react-native';
import InputField from '../components/form/InputField';
import NexArrowButton from '../components/buttons/NextArrowButton';
import Notification from '../components/Notification';
export default class LogIn extends Component{
constructor(props){
super(props);
this.state ={
formValid:false,
validEmail:false,
emailAddress:'',
validPassword:false,
}
this.handleNextButton = this.handleNextButton.bind(this)
this.handleCloseNotification = this.handleCloseNotification.bind(this)
this.handleEmailChange = this.handleEmailChange.bind(this);
}
static navigationOptions = {
headerLeft: () => (
<Button
onPress={()=>this.props.navigation.goBack()}
title="cancel"
color={colors.black}
/>
),
};
handleNextButton(){
if(this.state.emailAddress === 'admin#mail.com'){
this.setState({formValid:true})
} else{
this.setState({formValid: false});
}
}
handleCloseNotification(){
this.setState({formValid:true });
}
handleEmailChange(email){
const emailCheckRegex = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
const { validEmail } = this.state;
this.setState({ emailAddress: email });
if (!validEmail) {
if (emailCheckRegex.test(email)) {
this.setState({ validEmail: true });
}
} else if (!emailCheckRegex.test(email)) {
this.setState({ validEmail: false });
}
}
handlePasswordChange(password){
const { validPassword } = this.state;
this.setState({ password });
if (!validPassword) {
if (password.length > 4) {
// Password has to be at least 4 characters long
this.setState({ validPassword: true });
}
} else if (password <= 4) {
this.setState({ validPassword: false });
}
}
render(){
const {formValid, validPassword} = this.state;
const showNotification = formValid ? false:true;
const background = formValid ? colors.green01 : colors.darkOrange;
const notificationMarginTop = showNotification ? 10:0;
return(
<KeyboardAvoidingView style={[{backgroundColor:background}, styles.wrapper] } behavior="padding">
<View style={styles.ScrollViewWrapper}>
<ScrollView style={styles.ScrollView}>
<Text style={styles.loginHeader}>Log In</Text>
<InputField
labelText= "Email Address"
labelTextSize={20}
labelColor={colors.white}
textColor={colors.white}
borderBottomColor={colors.white}
inputType="email"
customStyle={{marginBottom:30}}
onChangeText={this.handleEmailChange}
/>
<InputField
labelText= "Password"
labelTextSize={20}
labelColor={colors.white}
textColor={colors.white}
borderBottomColor={colors.white}
inputType="password"
customStyle={{marginBottom:30}}
/>
</ScrollView>
<View style={styles.nextButton}>
<NexArrowButton
// handleNextButton={this.handleNextButton}
handleNextButton={()=>this.props.navigation.goBack()}
/>
</View>
<View style={[styles.notificationWrapper, {marginTop:notificationMarginTop}]}>
<Notification
showNotification={showNotification}
handleCloseNotification={this.handleCloseNotification}
type="Error"
firstLine="Those credentials don't look right."
secondLine="Please try again."
/>
</View>
</View>
</KeyboardAvoidingView>
)
}
}
const styles = StyleSheet.create({
wrapper:{
display:'flex',
flex:1,
},
ScrollViewWrapper:{
marginTop:60,
flex:1,
},
ScrollView:{
paddingLeft:30,
paddingRight:30,
paddingTop:10,
flex:1,
},
loginHeader:{
fontSize:34,
color:colors.white,
fontWeight:'300',
marginBottom:40,
},
nextButton:{
position:'absolute',
right:20,
bottom:20,
},
notificationWrapper:{
position: 'absolute',
bottom:0,
zIndex:9
}
});
The most confusing part is that the second snippet below of Login.js works perfectly and it is essentially the same thing which means that I am getting the props right, but still get the error in customised back button.
Second working snippet
<View style={styles.nextButton}>
<NexArrowButton
// handleNextButton={this.handleNextButton}
handleNextButton={()=>this.props.navigation.goBack()}
/>
</View>
App.js
import React, { Component } from 'react';
import { Platform, StyleSheet, Text, View } from 'react-native';
import LoggedOut from './src/screens/LoggedOut';
import LogIn from './src/screens/LogIn';
import LoggedIn from './src/screens/LoggedIn';
import {createAppContainer} from 'react-navigation';
import {createStackNavigator} from 'react-navigation-stack';
const RootStack = createStackNavigator(
{
LoggedOut: LoggedOut,
LogIn: LogIn,
},
{
initialRouteName: 'LoggedOut',
}
);
const AppContainer = createAppContainer(RootStack);
export default class App extends React.Component {
render() {
return <AppContainer />;
}
}
The error in more details
I really appreciate your help ! I am happy to provide more code if it makes it easier to debugg.
You have to change the static navigationOptions to following snippet if you want to access navigation properties in a static function:
static navigationOptions = ({ navigation }) => ({
headerLeft: () => (
<Button
onPress={()=>navigation.goBack()}
title="cancel"
color={colors.black}
/>
),
});
You don't need the this.props in this case ;) The static function does not have access to the this context so this.props will not work.
I have a problem when i try to navigation.navigate called RegisterPage but before that i try to typing anycharacter in field email and after that i click register button and back again, but the field is not empty
LoginContainer.js
import React, { Component, Fragment } from 'react';
import LoginComponent from '../../modules/LoginComponent/component/LoginComponent';
import axios from 'axios';
import { Toast } from '#ant-design/react-native';
class LoginContainer extends Component {
static navigationOptions = {
header: null,
}
constructor(props) {
super(props);
this.state = {
post : [],
email : '',
password : '' ,
showPassword: true,
}
}
onValueChange = (text, name) => {
this.setState({
[name] : text
});
}
getPostAPI = () => {
axios.get('http://10.2.62.212:3000/dataadmin')
.then((res) => {
this.setState ({
post : res.data,
})
})
.catch((err) => {
})
}
showPassword = () => {
const showPassword = !this.state.showPassword
this.setState({ showPassword });
}
onLoginPress = (event) => {
event.preventDefault();
const { post } = this.state;
if(!this.state.email.trim()) {
Toast.fail('Invalid Email', 1, undefined, false)
} else
if(!this.state.password.trim()) {
Toast.fail('Invalid Password', 1, undefined, false)
} else
if (post.find(e => `${e.email}${e.password}` === `${this.state.email}${this.state.password}` )) {
this.props.navigation.navigate('Dashboard');
} else
if (post.find(e => `${e.email}${e.password}` !== `${this.state.email}${this.state.password}` )) {
Toast.fail('Invalid Email or Password', 1, undefined, false)
}
}
onRegistPress = () => {
this.props.navigation.navigate('Register')
}
componentDidMount() {
this.getPostAPI();
}
render() {
return (
<Fragment>
<LoginComponent
navigation = {this.props.navigation}
change = {this.props.change}
onValueChange={this.onValueChange}
showPassword={this.showPassword}
onLoginPress={this.onLoginPress}
showPassword={this.state.showPassword}
onRegistPress = {this.onRegistPress}
/>
</Fragment>
);
}
}
export default LoginContainer;
LoginComponent.js
import React from 'react';
import { View, Text, TextInput, TouchableOpacity } from 'react-native';
import PropTypes from 'prop-types';
import { Button, Icon, Provider } from '#ant-design/react-native';
import styles from '../../../../assets/styles/default.style'
class LoginComponent extends React.Component {
static navigationOptions = {
header: null,
}
render() {
return (
<Provider >
<View style={styles.containerLogin}>
<View style={styles.parentViewStyleLogin}>
<Text style={styles.textHeaderStyle}> Merchant APP </Text>
<Text style={styles.textStyle}>Email</Text>
<View style={styles.inputContainer}>
<Icon style = {styles.styleIcon} name={"user"} color='black'/>
<TextInput
style={styles.textboxfield}
underlineColorAndroid='transparent'
onChangeText={(text) => this.props.onValueChange(text, 'email')}
placeholder = {'Input your email here'}
placeholderTextColor={'rgba(221,221,221,1)'}
/>
</View>
<Text style={[styles.textStyle, {marginTop: 20}] }> Password </Text>
<View style={styles.inputContainer}>
<Icon style = {styles.styleIcon} name={"key"} color='black'/>
<TextInput
style={styles.textboxfield}
secureTextEntry={this.props.showPassword}
underlineColorAndroid='transparent'
onChangeText={(text) => this.props.onValueChange(text, 'password')}
returnKeyType={'done'}
placeholder = {'Input your password here'}
placeholderTextColor={'rgba(221,221,221,1)'}
/>
</View>
<TouchableOpacity >
<Button style={[styles.buttonTextStyle, { borderRadius : 30 }]} onPress = {this.props.onLoginPress} >Login</Button>
</TouchableOpacity>
<TouchableOpacity onPress = {() => this.props.onRegistPress()} >
<Text style={styles.textDefault} >Dont have account ? Register</Text>
</TouchableOpacity>
</View>
</View>
</Provider>
);
}
}
export default LoginComponent
navigation
import { createStackNavigator } from 'react-navigation-stack';
import { createAppContainer } from 'react-navigation';
import LoginContainer from '../../app/LoginContainer/LoginContainer';
import DashboardContainer from '../../app/DashboardContainer/DashboardContainer';
import NotificationContainer from '../../app/NotificationContainer/NotificationContainer';
import TransactionContainer from '../../app/TransactionContainer/TransactionContainer'
import RegisterContainer from '../../app/RegisterContainer/RegisterContainer'
import DetailContainer from '../../app/DetailContainer/DetailContainer';
const AppNavigator = createStackNavigator(
{
Login: { screen: LoginContainer },
Dashboard: { screen: DashboardContainer },
Notification: { screen: NotificationContainer },
Transaction : { screen: TransactionContainer },
Register : { screen : RegisterContainer },
Detail : { screen : DetailContainer }
},
);
export default createAppContainer(AppNavigator);
this my ui
look after i try to type anycharacter and then directly click signup , and then click sigin again , and the field is not empty. i dont know how to fix it
there are 2 ways you can achieve this :
While you click on register in the DOnt have account text , then before navigation.navigate you can set the state of email to '' , like
onRegisterClick = () =>{
this.setSTate({email:''});//first this
this.props.navigation.navigate('Register');//then this
}
You can go back to the login page again by this.props.navigation.push('Login') rahther than navigation.navigate coz what navigate does is it calls the page from existing stack ,and push creates a new stack so values will be reset , so email will be null
I m getting error
TypeError: Cannot read property 'navigation' of undefined. I don't understand how to pass navigation component into each child so when a user presses an item it can navigate to employeeEdit component using React Navigation. i am newbie sorry if this is obvious.
import React, { Component } from 'react';
import { FlatList } from 'react-native';
import { connect } from 'react-redux';
//import { R } from 'ramda';
import _ from 'lodash';
import { employeesFetch } from '../actions';
import { HeaderButton } from './common';
import ListEmployee from './ListEmployee';
class EmployeeList extends Component {
static navigationOptions = ({ navigation }) => ({
headerRight: (
<HeaderButton onPress={() => navigation.navigate('employeeCreate')}>
Add
</HeaderButton>
)
});
componentWillMount() {
this.props.employeesFetch();
}
keyExtractor(item) {
return item.uid;
}
renderItem({ item }) {
return <ListEmployee employee={item} navigation={this.props.navigation} />;
}
render() {
return (
<FlatList
data={this.props.employees}
renderItem={this.renderItem} // Only for test
keyExtractor={this.keyExtractor}
navigation={this.props.navigation}
/>
);
}
}
const mapStateToProps = (state) => {
const employees = _.map(state.employees, (val, uid) => ({ ...val, uid }));
return { employees };
};
export default connect(mapStateToProps, { employeesFetch })(EmployeeList);
Here's the code for ListEmployee
import React, { Component } from 'react';
import {
Text,
StyleSheet,
TouchableWithoutFeedback,
View
} from 'react-native';
import { CardSection } from './common';
class ListEmployee extends Component {
render() {
const { employee } = this.props;
const { navigate } = this.props.navigation;
const { textStyle } = styles;
const { name } = this.props.employee;
return (
<TouchableWithoutFeedback onPress={() => navigate('employeeEdit', { employee })}>
<View>
<CardSection>
<Text style={textStyle}>{name}</Text>
</CardSection>
</View>
</TouchableWithoutFeedback>
);
}
}
/**
second argument in connect does 2 things. 1. dispatches all actions creators
return action objects to the store to be used by reducers; 2. creates props
of action creators to be used by components
**/
export default ListEmployee;
const styles = StyleSheet.create({
textStyle: {
fontSize: 18,
paddingLeft: 15,
}
});
This is one ES6 common pitfall. Don't worry my friend, you only have to learn it once to avoid them all over again.
Long story short, when you declare a method inside React Component, make it arrow function
So, change from this.
renderItem({ item }) {
to this
renderItem = ({ item }) => {
That should solve your problem, for some inconvenient reason, you can only access "this" if you declare your method as an arrow function, but not with normal declaration.
In your case, since renderItem is not an arrow function, "this" is not referred to the react component, therefore "this.props" is likely to be undefined, that is why it gave you this error Cannot read property 'navigation' of undefined since
this.props.navigation = (undefined).navigation
Inside your renderItem method, you can manage what happens when the user presses one an item of your FlatList:
renderItem({ item }) {
<TouchableOpacity onPress={() => { this.props.navigator.push({id: 'employeeEdit'})}} >
<ListEmployee employee={item} navigation={this.props.navigation} />
</TouchableOpacity>
}
Hope it help you!
A navigation sample
here VendorList is the structure rendered
<FlatList
numColumns={6}
data={state.vendoreList}
keyExtractor={(data) => data.id}
renderItem={({ item }) =>
<TouchableOpacity onPress={() => props.navigation.navigate("Home1")} >
<VendorList item={item} />
</TouchableOpacity>
}
/>
in ListEmployee
const {navigation}= this.props.navigation;
this use
<TouchableWithoutFeedback onPress={() => navigation.navigate('employeeEdit', { employee })}>
just need to modification on those two lines, i make text bold what changes you need to do
I am trying to update ListView.DataSource after fetching data from server but its not happening. I have two components, one is imported in other.
From base component I am trying to update ListView.DataSource in other component. Here is my code.
index.android.js
import React, { Component } from "react";
import { ListView, View, Button, AppRegistry } from "react-native";
import OtherComponent from "./Components/OtherComponent";
class MyApp extends Component {
constructor(props) {
super(props);
this.state = {
movies: [{ title: "ABCD" }, { title: "EFGH" }]
};
}
getTopics = () => {
fetch("https://facebook.github.io/react-native/movies.json")
.then(response => response.json())
.then(responseText => {
console.log(responseText.movies);
this.setState({
movies: responseText.movies
});
})
.catch(error => {
console.warn(error);
});
};
render() {
return (
<View>
<Button
onPress={this.getTopics}
title="Get Topics"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/>
<OtherComponent movies={this.state.movies} />
</View>
);
}
}
AppRegistry.registerComponent("MyApp", () => MyApp);
OtheComponent.js
import React, { Component } from "react";
import { View, ListView, Text, StyleSheet, Button } from "react-native";
export default class FormativeRevisionList extends Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
});
this.state = {
dataSource: ds.cloneWithRows(props.movies)
};
}
render() {
return (
<View>
<ListView
style={styles.listContainer}
dataSource={this.state.dataSource}
renderRow={rowData => (
<View>
<Text style={styles.listItem}>{rowData.title}</Text>
</View>
)}
/>
</View>
);
}
}
const styles = StyleSheet.create({
listContainer: {
paddingTop: 22
},
listItem: {
fontSize: 30,
fontWeight: "bold",
textAlign: "center"
}
});
In your code, you only set your dataSource in contructor of FormativeRevisionList, this mean FormativeRevisionList will only render the given movies when your first render it.
To render new list after you press Get Topics button , you need to set the dataSource again when it receive new props, this can be achieve by setting it in FormativeRevisionList componentWillReceiveProps
componentWillReceiveProps(nextProps) {
if (nextProps.movies !== this.props.movies) {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(nextProps.movies)
})
}
}
You can read more about componentWillReceiveProps from here