How to null check and navigate to new screen on click submit when textinput and submit button both are in different js files in react native? - react-native

render(){
return (
<ImageBackground
source={require('../images/back02.png')}
style={styles.bgscreen}
>
<KeyboardAvoidingView behavior='position'>
<Image
style={styles.headImage}
source={require('../images/login_img.png')}
/>
<ImgBack/>
</KeyboardAvoidingView>
<BottomButton navigation={this.props.navigation} />
</ImageBackground>
);}
}
ImgBack contains username and password textinput.
BottomButton contains the submit button
I want to navigate to new activity when submit button clicked. navigation to new screen is working perfectly but before navigating i want to null check the TextInput which are on
I am new to React-Native. Complete Beginner here. I want even know what to do. Help.
ImgBack.js file
class imgBack extends React.Component {
constructor()
{
super();
this.state = { hidePassword: true }
}
managePasswordVisibility = () =>
{
this.setState({ hidePassword: !this.state.hidePassword });
}
usernameValidate = (EnteredValue) =>{
var TextLength = EnteredValue.length.toString();
if(TextLength == 10 ){
Alert.alert("Sorry, You have reached the maximum input limit.")
}
else if(TextLength == 0){
Alert.alert("Username can't be blank")
}
}
passValidate = (EnteredValue) =>{
var TextLength = EnteredValue.length.toString();
if(TextLength == 10 ){
Alert.alert("Sorry, You have reached the maximum input limit.")
}
else if(TextLength == 0){
Alert.alert("Username can't be blank")
}
}
render(){
return (
<ImageBackground resizeMode='contain'
source={require('../images/login_back.png')}
style={{
marginHorizontal: 10,
height: 290,
padding: 30,
}}>
<View style={
styles.textInputContainer
} >
<TextInput
placeholder="Username"
underlineColorAndroid = "transparent"
placeholderTextColor="#000000"
maxLength={10}
onChangeText={ EnteredValue => this.usernameValidate(EnteredValue) }
/>
</View>
<View style = { styles.textInputContainer }>
<TextInput
onChangeText={ EnteredValue => this.passValidate(EnteredValue) }
underlineColorAndroid = "transparent"
secureTextEntry = { this.state.hidePassword }
placeholder="Password"
placeholderTextColor="#000"
/>
<TouchableOpacity style = { styles.visibilityBtn } onPress = { this.managePasswordVisibility }>
<Image source = { ( this.state.hidePassword ) ? require('../images/eye_close_icon.imageset/eye_close_icon.png') : require('../images/eye_icon.imageset/eye_icon.png') } style = { styles.btnImage } />
</TouchableOpacity>
</View>
</ImageBackground>
)
}
} ```
**Bottombutton File**
class bottomButon extends Component {
render(){
return (
<ImageBackground
style={{ height: 80, marginLeft: '20%', marginTop: 10 }}
resizeMode={'center'}
source={require('../images/btn_back.png')} >
<TouchableOpacity
onPress={ this.login } >
<Text style={{ textAlign: 'center', marginTop: 25 }}>Submit & SYNC</Text>
</TouchableOpacity>
</ImageBackground>
)
} }
export default bottomButon; ```

solution:
constructor(props) {
super(props);
this.state = {
hidePassword: true,
username: '',
password: '',
};
}
validUserPass = async () => {
try {
if (this.state.username === '') {
Alert.alert('Username is required !');
} else if (this.state.password === '') {
Alert.alert('Password is required !');
} else {
this.props.navigation.navigate('selectEmoji');
}
} catch (error) {
console.warn(error);
}
};
it helped me solve my issue.

Related

react native flatlist is slow with dynamic items and a big data

I have implemented a list like Instagram explore page using this question React native grid like instagram
but it is not performant at all with the layout I have. is there any other way to implement this?
the other problem is in re-rendering different items for some rows when scrolling, I manage to reduce it by changing the conditions in which item should be rendered but there is still a problem.
I have uploaded a video of my app here: https://youtu.be/gP7kbwKDeNA
I chunck my content to 3 item arrays and then pass it to this component this is my code:
const albumIcon = require("../../assets/icons/stream.png");
const videoIcon = require("../../assets/icons/play.png");
const { width } = Dimensions.get("window");
let isLeft = false;
let url = null;
let icon = null;
function CustomGridList(props){
const onRefresh = (loadMore = false) => {
if (loadMore === true) {
loadMore();
} else {
props.refresh(props.query);
}
};
const loadMore = () => {
props.refresh(props.query, true);
};
const getItemKey = (index,item) => {
return "item".concat(index);
};
const _renderPhoto = (item, isLarge) => {
url = null;
icon = null;
if (item && item.videos.length > 0) {
url = item.videos[0].thumbnail;
icon = videoIcon;
} else if (item && item.images.length > 1) {
url = item.images[0].url;
icon = albumIcon;
} else if(item && item.images.length > 0){
url = item.images[0].url;
}else{
url = 'https://castlebba.co.uk/wp-content/uploads/2017/04/default-image-620x600.jpg'
}
return (
<TouchableOpacity style={{marginRight: 1}}>
<CachedImage
source={{uri: url}}
style={{ flex: 1}}
afterClickFunc={() => console.log('clicked')}
useTouchableOpacity
width={isLarge ? (width*(2/3)+4) : width/3}
height={isLarge ? 200: 100}
/>
<View
style={{
position: 'absolute',
top: 4,
right: 4,
}}
>
<CustomIcon boldColor={'#fff'} icon={icon} />
</View>
</TouchableOpacity>
);
};
const _renderRow = (index, item) => {
console.log(index, item );
console.log('indexable -> ', ++index%3 );
let item1 = item[0] !== null? item[0]: null;
let item2 = item[1] !== null? item[1]: null;
let item3 = item[2] !== null? item[2]: null;
if((index+1) % 3 === 0){
if (isLeft){
isLeft = false;
}else{
isLeft = true;
}
return (
<View style={{flexDirection: isLeft? 'row' : 'row-reverse', height: 200}}>
{_renderPhoto(item1, true)}
<View style={{flexDirection: 'column'}}>
<View style={{flex:1, height: 100}}>
{_renderPhoto(item2,false)}
</View>
<View style={{ flex:1, height: 100}}>
{_renderPhoto(item3,false)}
</View>
</View>
</View>
);
}else{
return (
<View style={{flexDirection: 'row', height: 100}}>
{_renderPhoto(item1, false)}
{_renderPhoto(item2, false)}
{_renderPhoto(item3, false)}
</View>
);
}
}
const { loading, content } = props;
if(loading && !content) {
return (
<View style={{ marginTop: 30, height: 100, alignItems: 'center' }}>
<ActivityIndicator
color={getThemeStyle().color_main}
size={'large'}
/>
</View>
);
}
if (content === null) {
return <View />;
}
return (
<View style={styles.flatListUserListWrapper}>
<View style={styles.albumContainer}>
<CustomFlatList
{...props}
showsVerticalScrollIndicator={false}
style={[
styles.albumContainer,
content.length > 0 ? { margin: 1 } : {},
]}
content={content}
renderItem={({ index,item }) => _renderRow(index,item)}
itemSeparator={() => <View style={{ width: '100%', height: 1 }} />}
keyExtractor={(index,item) => getItemKey(item)}
enableLoadMore={true}
loading={loading}
onRefresh={() => onRefresh()}
loadMore={()=>loadMore()}
pagePostType={"search"}
canSendPost={false}
/>
</View>
</View>
);
}
CustomGridList.propTypes = {
error: PropTypes.string,
loading: PropTypes.bool,
refresh: PropTypes.func,
navigation: PropTypes.object,
content: PropTypes.array,
query: PropTypes.string,
navigateTo: PropTypes.func, //TODO implement this one on click
};
export default CustomGridList = React.memo(CustomGridList);
I separated different rows and put them in different files then changed the way I was selecting rows with big tiles, it got a little bit better but still rerendering more than it's supposed to. I'm not doing anything dynamicly.
const albumIcon = require("../../../assets/icons/stream.png");
const videoIcon = require("../../../assets/icons/play.png");
const { width } = Dimensions.get("window");
let isLeft = false;
let url = null;
let icon = null;
function CustomGridList(props){
const onRefresh = (loadMore = false) => {
if (loadMore === true) {
loadMore();
} else {
props.refresh(props.query);
}
};
const loadMore = () => {
props.refresh(props.query, true);
};
const getItemKey = (index,item) => {
return "item".concat(index);
};
const _renderRow = (index, item) => {
if((index+1)%6 === 0){
return(<LargeTiledRow item={item} width={width} reverse={false} navigateTo={props.navigateTo}/>)
}else if((index+4)%6 === 0){
return(<LargeTiledRow item={item} width={width} reverse={true} navigateTo={props.navigateTo}/>)
}else{
return (<GridSimpleRow item={item} width={width} navigateTo={props.navigateTo}/>);
}
}
const { loading, content } = props;
if(loading && !content) {
return (
<View style={{ marginTop: 30, height: 100, alignItems: 'center' }}>
<ActivityIndicator
color={getThemeStyle().color_main}
size={'large'}
/>
</View>
);
}
if (content === null) {
return <View />;
}
return (
<View style={styles.flatListUserListWrapper}>
<View style={styles.albumContainer}>
<CustomFlatList
{...props}
showsVerticalScrollIndicator={false}
style={[
styles.albumContainer,
content.length > 0 ? { margin: 1 } : {},
]}
content={content}
renderItem={({ index,item }) => _renderRow(index,item)}
itemSeparator={() => <View style={{ width: '100%', height: 1 }} />}
keyExtractor={(index,item) => getItemKey(item)}
enableLoadMore={true}
loading={loading}
onRefresh={() => onRefresh()}
loadMore={()=>loadMore()}
pagePostType={"search"}
canSendPost={false}
/>
</View>
</View>
);
}
function areEqual(prevProps, nextProps) {
prevProps.content === nextProps.content
}
export default CustomGridList = React.memo(CustomGridList,areEqual);

Possible Unhandled Promise Rejection (id: 0): TypeError: undefined is not an object (evaluating '_this.props.navigation.navigate')

I'm trying to display an image I have captured using expo-camera, from the camera component I'm trying to navigate to a new file which will display the image but after I took the image it won't navigate to the new page.
I tried importing the file and then navigate it but it still won't work and give me the warning instead.
This is the code where I tried to navigate to the new file.
export default class CameraExample extends React.Component {
state = {
hasCameraPermission: null,
type: Camera.Constants.Type.back,
};
async componentDidMount() {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ hasCameraPermission: status === 'granted' });
}
snap = async() => {
if(this.camera) {
console.log('Taking photo');
const options = {quality: 1, base64: true, fixOrientation: true, exif: true};
const photo = await this.camera.takePictureAsync(options);
this.props.navigation.navigate("Show", {photouri: photo.uri})
}
}
render() {
const { hasCameraPermission } = this.state;
if (hasCameraPermission === null) {
return <View />;
} else if (hasCameraPermission === false) {
return <Text>No access to camera</Text>;
} else {
return (
<View style={{ flex: 1 }}>
<Camera style={{ flex: 1 }} type={this.state.type}
ref = {ref => {
this.camera = ref;
}}
>
<View
style={{
flex: 1,
backgroundColor: 'transparent',
flexDirection: 'row',
}}>
<TouchableOpacity onPress={this.snap.bind(this)}>
<Ionicons
name = "md-camera"
color = "white"
size = {30}
/>
</TouchableOpacity>
<TouchableOpacity
style={{
flex: 0.1,
alignSelf: 'flex-end',
alignItems: 'center',
}}
onPress={() => {
this.setState({
type:
this.state.type === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back,
});
}}>
<Ionicons
name = "md-reverse-camera"
color = "white"
size = {30}
/>
</TouchableOpacity>
</View>
</Camera>
</View>
);
}
}
}
And this is the code where I try to display the image.
export default class ShowImages extends React.Component{
render(){
console.log('OK')
const { navigation } = this.props;
const paramm = navigation.getParam('photouri');
return(
<Content>
<View>
<Text>
paramm: {JSON.stringify(paramm)}
</Text>
<Image style={{height: 700, width: 850, alignSelf: "center"}}
source={{uri: this.props.navigation.state.paramm.photouri}}
resizeMode="contain"/>
</View>
</Content>
)
}
}
I expect it to navigate to the new page and display the captured
image but it gave me the warning. I can't seem to find what is wrong with my code. Can anyone suggest what I should do? Thank you.
change this
<TouchableOpacity onPress={this.snap.bind(this)}> => <TouchableOpacity onPress={this.snap}>
Put it in the status value and pass it on.
export default class ShowImages extends React.Component{
constructor(props) {
super(props);
this.state = {
paramm: this.props.navigation.state.params.photouri
};
}
...
<Image style={{height: 700, width: 850, alignSelf: "center"}}
source={{uri: this.state.paramm }}
resizeMode="contain"/>

change custom radio button image onclick

I'm trying to create a button that can change the image when it is clicked, I've tried with changing its opacity now I'm trying to change the image. Here's what I've done for the custom button
const styles = StyleSheet.create({
container: {
marginVertical: normalize(5),
opacity: 0.5
},
activeButton: {
opacity: 1
}
});
export default class RadioButton extends Component {
constructor(props) {
super(props);
this.state = {
selected: this.props.currentSelection === this.props.value,
}
}
componentDidUpdate(prevProps) {
if (this.props.currentSelection !== prevProps.currentSelection) {
this.setState({ selected: this.props.currentSelection === this.props.value });
}
}
render() {
let activeButton = this.props.activeStyle ? this.props.activeStyle : styles.activeButton;
return (
<View style={[styles.container, this.props.containerStyle, this.state.selected ? activeButton : null]}>
<TouchableOpacity onPress={() => {
this.setState({ selected: !this.state.selected });
this.props.onPress();
}}>
{
this.props.element ?
this.props.element :
<Text> {this.props.value} </Text>
}
</TouchableOpacity>
</View>
);
}
}
It should be like this image
First import both images. one for selected and another for normal state.
import selectedImg from '../images/selected.png';
import normalImg from '../images/normal.png';
and then add below method to your component
btnBackgroundImage() = {
var imgSource = this.state.selected? selectedImg : normalImg;
return (
<Image
style={'put style here'}
source={ imgSource }
/>
);
}
now edit your render method like below
render() {
let activeButton = this.props.activeStyle ? this.props.activeStyle : styles.activeButton;
return (
<View style={[styles.container, this.props.containerStyle, this.state.selected ? activeButton : null]}>
<TouchableOpacity onPress={() => {
this.setState({ selected: !this.state.selected });
this.props.onPress();
}}>
{
this.btnBackgroundImage()
}
</TouchableOpacity>
</View>
);
}

react native navigation via a button in a modal

In my application, I want to navigate to another screen from a button in a modal. I tried the normal workout and it didn't work. The modal closes properly but the navigation doesn't happen. What am I doing wrong here? Im trying to navigate from navigateToMainFeed() and it stays in the same screen without navigating.
class TrainerRegistraionScreen extends Component {
constructor(props) {
super(props);
this.state = {
statement: {
value: "",
valid: false,
touched: false,
validationRules: {
minLength: 1
}
},
keyboardVisible: false,
buttonTouched: false,
displayModal: false
}
}
// handle statement changes
statementChangedHandler = value => {
this.setState(prevState => {
return {
statement: {
...prevState.statement,
value: value,
touched: true,
valid: validate(value, prevState.statement.validationRules)
},
buttonTouched: false
};
});
};
// check for empty fields of the screen
_getFiledsNotEmptyStatus = () => {
return this.state.statement.value.length > 0 ? true : false;
}
// display message
_displayMessage = () => {
let { keyboardVisible } = this.state;
let content = (
<View style={{ width: "90%", marginBottom: 10, alignSelf: 'center' }}>
<Text style={{ fontSize: 16, color: "#707070", fontWeight: "300" }}>
Please write a short statement about your focuses as a trainer.
It is important to show your capabilities and knowledge base in
exercise and nutrition science. Inclue all certification details.
</Text>
</View>
);
if (keyboardVisible) {
content = null;
}
return content;
}
// navigate to main feed
navigateToMainFeed = () => {
this.props.navigation.navigate("mainfeed");
this.setState({
displayModal: false
});
}
// register trainer
async registerTrainer() {
let {
firstName,
lastName,
email,
password
} = this.props;
this.setState({
buttonTouched: true
});
let trainer = {
firstName: firstName,
lastName: lastName,
email: email,
password: password
}
await this.props.registerTrainer(trainer);
if (!this.props.signUpHasError) {
this.setState({
displayModal: true
});
}
}
// render popup modal
_renderPopupModal() {
return (
<ModalPopup visible={this.state.displayModal}>
<TrainerMessage
onPress={() => this.navigateToMainFeed()}
/>
</ModalPopup>
);
}
// render
render() {
let { userRegistrationInProgress } = this.props;
return (
<View>
<ImageBackground source={BackgroundOnly} style={{ width: width, height: height }} >
<View style={{ marginTop: "5%" }}>
<ClickableIcon
source={BackArrow}
height={30}
width={30}
onIconPressed={() => {this.props.navigation.navigate("trainerExperience");}}
/>
</View>
<View style={styles.headerStyles}>
<HeadingText
fontSize={26}
fontWeight={300}
textAlign="left"
fontColor="#707070"
>
Personal Statement
</HeadingText>
</View>
<View>
{this._displayMessage()}
</View>
<View style={styles.detailInput}>
<DetailInput
inputStyle={styles.inputStyles}
height={120}
width={width - 40}
multiline={true}
numberOfLines={6}
underlineColorAndroid="transparent"
maxLength={500}
editable={!userRegistrationInProgress}
onChangeText={value => this.statementChangedHandler(value)}
/>
</View>
<View>
<RoundedButton
buttonStyle={styles.buttonStyle}
text="FINISH"
submitting={userRegistrationInProgress}
onPress={() => this.registerTrainer()}
disabled={!this._getFiledsNotEmptyStatus()}
/>
</View>
{this._renderPopupModal()}
</ImageBackground>
</View>
);
}
static navigationOptions = { header: null }
}
First close the modal and navigate the screen
this.setState({
displayModal: false
},()=>{
this.props.navigation.navigate("mainfeed");
});

React Native - How to re-render Navigator component on icon press

How would I re-render/ change the colour of a Navigator component icon when it is pressed. I have TouchableHighlight on it but I need the state to stay permanently. For example someone presses a 'favourite' heart icon, and it turns from grey to red. I've been trying to figure out a solution for some time now.
This is the section of my code where the change should happen. I need my if statement and Navigator icon to re-render based on the true/false value which changes when the TouchableHighlight onPress triggers.
var NavigationBarRouteMapper = {
RightButton: function(route, navigator, index, navState) {
// * The button displayed in the top right of the navigator
switch(route.id) {
case('businessProfile'):
if (route.isFavourited) {
return (
<View style={[ styles.multipleIcons ]}>
<TouchableOpacity
onPress={() => {
var favouriteBusiness = new FavouriteBusiness();
favouriteBusiness.updateBusinessAsFavourite(route.business.id)
}}
style={[ styles.navBarButton, styles.iconRightPaddingLarge ]}
>
<IconFA
name='heart'
size={ 25 }
color='#de6262'
style={ styles.icon }
/>
</TouchableOpacity>
</View>
);
} else {
return (
<View style={[ styles.multipleIcons ]}>
<TouchableOpacity
onPress={() => {
var favouriteBusiness = new FavouriteBusiness();
favouriteBusiness.updateBusinessAsFavourite(route.business.id)
}}
style={[ styles.navBarButton, styles.iconRightPaddingLarge ]}
>
<IconFA
name='heart'
size={ 25 }
color='#ddd'
style={ styles.icon }
/>
</TouchableOpacity>
</View>
);
}
default:
return null;
}
},
};
<Navigator
ref='navigator'
sceneStyle={ [styles.container, styles.inner] }
initialRoute={{
id: this.props.scene,
title:this.props.title
}}
renderScene={ this.renderScene }
configureScene={(route) => {
if (route.sceneConfig) {
return route.sceneConfig;
}
return Navigator.SceneConfigs.FloatFromRight;
}}
navigationBar={
<Navigator.NavigationBar
routeMapper={ NavigationBarRouteMapper }
style={ styles.navigationBar }
/>
}
/>
if you still did not get the solution perhaps these could help.
you can define some Properties in construktor:
constructor(props) {
super(props);
this.state = {
color: 'lightgrey',
};
}
You can define the Icon in the RooteMapper of the Navigaton bar:
return (<Icon
name="star"
style={[styles.rightHadderButton, {color: this.state.color}]}
onPress={RightNavigatorButton.favoriteStar.bind(this)}/>);
and then change the collor in the onpress defined Function:
exports.favoriteStar = function(event) {
if (this.state.color === 'lightgrey') {
this.setState({ color: '#990000' });
AlertIOS.alert( "favoriteStar","Favorite");
} else {
this.setState({ color: 'lightgrey' });
AlertIOS.alert( "favoriteStar","notFavorite");
}
}
Hope these works for you too and it helps you.