React-Native, Redux, invariant violation - reactjs

import React, { Component } from "react";
import { connect } from "react-redux";
import { Text, View, TouchableOpacity, StyleSheet,Image ,TextInput,Button} from "react-native";
import Icon from 'react-native-vector-icons';
import { login } from "../Actions/actionCreator";
class LoginScreen extends Component {
static navigationOptions = {
title: "Login"
};
constructor(props){
super(props);
this.state = {
username:'',
password:''
}
}
doLogin(username,password)
{
}
render() {
return (
<View style={styles.rootContainer}>
<Image
style={styles.logo}
source={require('/Users/apple/Desktop/redux-react-navigation-demos-authFlow/src/images/logo.png')}
/>
<Icon
name='user'
color='#000'
size={14}
/>
<TextInput
onChangeText={username => this.setState({ username })}
value={this.state.username}
style={styles.textInput}
placeHolder="Username"
/>
<TextInput
onChangeText={password => this.setState({ password })}
value={this.state.password}
style={styles.textInput}
placeHolder="Password"
/>
<Text style={styles.textGreen}>Forget Your Password</Text>
<Button
onPress={() => {
this.doLogin(this.state.username, this.state.password)
}}
color="#ffffff"
title="Sign In"
>
Sign In
</Button>
</View>
);
}
}
const styles = StyleSheet.create({
rootContainer: {
flex: 1,
backgroundColor: "cyan",
justifyContent: "center",
alignItems: "center"
},
textStyles: {
textAlign: "center",
color: "rgba(0,0,0,0.8)",
fontSize: 16
},
touchableStyles: {
marginTop: 15,
backgroundColor: "black",
paddingHorizontal: 50,
paddingVertical: 10,
borderRadius: 5
},
textGreen: {
textAlign:"center",
fontSize:16,
color:'#7dc4a6'
},
logo: {
width: 587,
height: 112
},
textInput: {
borderColor:'black',
backgroundColor:'#D3D3D3',
width:300,
borderWidth: 1,
borderStyle: 'solid',
fontSize:15,
borderRadius: 25,
}
});
const mapDispatchToProps = {
login
};
const Login = connect(null, mapDispatchToProps)(LoginScreen);
export default Login;
The above code is my LoginScreen Component I have exported my login screen please check. My simulator throws an error saying invariant violation saying that I have not exported my component. I have a loginScreen Component which is what I want to export. I am importing this
component in my navigationStack for routing and navigation purposes.
Screenshot of the error:

Do not export your component with default. Just use export Login or export const Login = connect(null, mapDispatchToProps)(LoginScreen);.
Then, in your StackNavigator, import it: import { Login } from 'src/components/Login.
This should solve your problem of your imported comp's instance being undefined.

If you use "default" with export i.e.
export default Login;
Then you should import it like:
import Login from 'src/components/Login'
and if you didn't use "default", try to import like:
import {Login} from 'src/components/Login'

Most probably the Icon import is wrong so that ends up being undefined, triggering the error.
Assuming you're using this package: https://github.com/oblador/react-native-vector-icons , the correct import would be:
import Icon from 'react-native-vector-icons/FontAwesome';
(notice the /FontAwesome addition at the end)
As a general debugging method, whenever you get this error, then inside render() (or even outside the class) do a console.log of all the variables you're using as tags. Usually one of them is undefined, and you've found the culprit. Make sure the import works and yields a Component or a non-empty string and you're good to go :)

Related

Switch navigator does not works in React Native

I've asked a question already, and modified as much as I can, but still in trouble.
Combined all children Screen js files into App.js
When I compile and run, App shows LoginScreen.js, not LoadingScreen.js.
Also, onPress={this.props.navigation.navigate('Loading')} does not works. If I press the button, it shows nothing.
What am I still missing?
import React from 'react'
import { StyleSheet, Platform, Image, Text, View, TextInput, Button, ActivityIndicator } from 'react-native'
import { createAppContainer, createSwitchNavigator } from 'react-navigation'
import { createBottomTabNavigator } from 'react-navigation-tabs';
import Ionicons from 'react-native-vector-icons/Ionicons';
class LoadingScreen extends React.Component {
render() {
return (
<View style={styles.loadingcontainer}>
<Text>Loading</Text>
<ActivityIndicator size="large" />
<Button title="Move to LoginScreen" onPress={this.props.navigation.navigate('Login')} />
</View>
)
}
}
class LoginScreen extends React.Component {
state = { email: '', password: '' }
render() {
return (
<View style={styles.logincontainer}>
<Button
title='Sign in' onPress={this.props.navigation.navigate('Loading')}
/>
<Button
title='Sign Up'
/>
</View>
)
}
}
const styles = StyleSheet.create({
loadingcontainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
logincontainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
logintextInput: {
height: 40,
width: '90%',
borderColor: 'gray',
borderWidth: 1,
marginTop: 8
},
})
const App = createSwitchNavigator(
{
Loading: {
screen: LoadingScreen,
},
Login: {
screen: LoginScreen,
},
}
);
export default createAppContainer(App);
Thank you for your help.
For just navigation, you can use a stack navigator. The switch navigator used for conditional rendering of two different stacks. Anyways you can set loading as the first screen buy setting initialRouteName to loading. Here is an example
createSwitchNavigator(
{
LimitedAccess: {
screen: Trial,
},
AppScreens: {
screen: AppScreens,
},
AuthScreens: {
screen: AuthScreens,
},
},
{
initialRouteName: signedIn ? 'AppScreens' : 'AuthScreens',
},
),
);
Note: Here signedIn is a conditional operator which decides the rendering of stacks.
The way your props are currently defined causes them to be instantly executed.
The onPress prop is instantly executed.
return (
<View style={styles.loadingcontainer}>
<Text>Loading</Text>
<ActivityIndicator size="large" />
<Button title="Move to LoginScreen" onPress={this.props.navigation.navigate('Login')} />
</View>
)
You should instead attach a function to onPress that can be executed when the button is pressed.
return (
<View style={styles.loadingcontainer}>
<Text>Loading</Text>
<ActivityIndicator size="large" />
<Button title="Move to LoginScreen" onPress={() => this.props.navigation.navigate('Login')} />
</View>
)
Your onPress-calls are running instantly, which causes your problems.
Change to:
import React from 'react'
import { StyleSheet, Platform, Image, Text, View, TextInput, Button, ActivityIndicator } from 'react-native'
import { createAppContainer, createSwitchNavigator } from 'react-navigation'
import { createBottomTabNavigator } from 'react-navigation-tabs';
import Ionicons from 'react-native-vector-icons/Ionicons';
class LoadingScreen extends React.Component {
render() {
return (
<View style={styles.loadingcontainer}>
<Text>Loading</Text>
<ActivityIndicator size="large" />
<Button title="Move to LoginScreen" onPress={() => this.props.navigation.navigate('Login')} />
</View>
)
}
}
class LoginScreen extends React.Component {
state = { email: '', password: '' }
render() {
return (
<View style={styles.logincontainer}>
<Button
title='Sign in' onPress={() => this.props.navigation.navigate('Loading')}
/>
<Button
title='Sign Up'
/>
</View>
)
}
}
const styles = StyleSheet.create({
loadingcontainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
logincontainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
logintextInput: {
height: 40,
width: '90%',
borderColor: 'gray',
borderWidth: 1,
marginTop: 8
},
})
const App = createSwitchNavigator(
{
Loading: {
screen: LoadingScreen,
},
Login: {
screen: LoginScreen,
},
}
);
export default createAppContainer(App);

Pass Function to Functional Component in React Native

I have an ItemForm component in a react-native expo app I would like to use for both Creates and Updates. It is a functional component which I am trying to pass a handler from a parent component to either create a new item or update an existing item. I am having trouble understanding how to pass the onPress handler down from the parent Class Component down to the functional child form Component.
Parent Component:
import React, { Component } from "react";
import { Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
// You can import from local files
import ItemForm from './components/ItemForm';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default class App extends Component {
handleSubmit = ({ name }) => {
alert(name);
// todo: get form data and create or update database
}
render() {
return (
<View style={styles.container}>
<Card>
<ItemForm onSubmit={() => this.handleSubmit}/>
</Card>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
});
ItemForm.js
import React, { useState } from "react";
import { View, Text, StyleSheet } from "react-native";
import { Button, Card, Icon, Input } from "react-native-elements";
export default function ItemForm({ onSubmit }) {
const [name, setName] = useState("");
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerText}>Add an Item</Text>
</View>
<Card
containerStyle={{
height: "80%",
margin: 0,
elevation: 1,
borderWidth: 0,
shadowOpacity: 0,
shadowRadius: 0,
}}
>
<Input
label="Item Name"
labelStyle={styles.itemText}
placeholder="Name Here"
onChangeText={(name) => setName(name)}
/>
<Button
icon={
<Icon
name="check-circle"
size={20}
style={{ paddingRight: 10 }}
onPress={onSubmit({name: name})}
/>
}
title="Save Item"
/>
</Card>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "column",
},
header: {
alignItems: "center",
paddingTop: 40,
},
headerText: {
fontSize: 32,
},
itemText: {
fontSize: 25,
}
});
This is currently throwing:
onSubmit is not a function. (In 'onSubmit({name: name})', 'onSubmit' is undefined)
I've tried various different syntaxes of passing or calling the function by reading the docs here but I run into a lot of errors like Please attach a method to this component and I can't seem to get past this?
onPress should be a function that calls onSubmit, not the return value of calling onSubmit (onPress={onSubmit({name: name})}).
onPress needs to be on the Button.
You need to pass down this.handleSubmit from the parent component, and not a function that returns this.handleSubmit (onSubmit={() => this.handleSubmit}):
So in App:
<ItemForm onSubmit={this.handleSubmit}/>
In ItemForm:
<Button
onPress={() => onSubmit({name: name})}
icon={
<Icon
name="check-circle"
size={20}
style={{ paddingRight: 10 }}
/>
}
title="Save Item"
/>
demo

How to overcome the single default export issue when using react-nav and redux

I have a button in my app that needs to navigate to another screen and change a value in redux simultaneously. Of course, export default cannot be used twice but - when I mark one of the exports as a constant, the code will fail. In this case, connect(null, mapDispatchToProps) is failing. How can I get the two exports working?:
import React, { Component } from 'react';
import { View, Image, StyleSheet } from 'react-native';
import { person } from "../../assets/people";
import { withNavigation } from 'react-navigation';
import { connect } from 'react-redux';
export class ProfileCard extends Component {
updateMarkerState = (x) => {
this.props.updateMarkerState(x)
this.props.navigation.navigate("map");
}
render() {
return (
<Card >
<CardItem cardBody>
<Image source={this.props.data.values.photoId} style={{ height: 200, width: null, flex: 1 }} />
</CardItem>
<CardItem>
<Text>{this.props.data.values.altName}</Text>
</CardItem>
<CardItem>
<Text>{this.props.data.values.personIntroText}</Text>
</CardItem>
<CardItem style={styles.buttonContainer}>
<Button transparent
onPress={() =>
this.updateMarkerState(this.props.data.values.id)
}>
<Icon active name="pin" />
</Button>
<Button transparent
onPress={() => {
this.props.navigation.navigate("YouTubeScreen", {id: this.props.data.values.id});
}}>
<Icon active name="play"/>
</Button>
</CardItem>
</Card>
);
}
}
const styles = StyleSheet.create({
buttonContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'flex-end',
alignItems: 'center',
}
});
const mapDispatchToProps = (dispatch) => {
return {
updateMarkerState: (id) => {
dispatch(updateMarkerState(id))
}
}
}
export const conn = connect(null, mapDispatchToProps)(ProfileCard)
export default withNavigation(ProfileCard);
You can concatenate HOCs, like
export default withNavigation(connect(null, mapDispatchToProps)(ProfileCard));

Invariant Violation: Element type is invalid

I am trying to clean up my styles by using a StyleSheet, but I can't seem to get it to work. I believe the error (seen below) is caused when I attempt to create a StyleSheet (const styles = EStyleSheet.create.
Note I am using react-native-extended-stylesheet However this is not the problem. This also happens with react-native's stylesheet.
This a picture of the error:
This this my code:
import React, { Component } from "react";
import {
View,
Text,
Button,
Image,
StyleSheet,
TextInput,
KeyboardAvoidingView,
TouchableOpacity
} from "react-native";
export default class Login extends Component {
render() {
return (
<View style={styles.wrapper}>
<Text>Login screen </Text>
<KeyboardAvoidingView behavior="padding" style={styles.loginContainer}>
<TextInput
placeholder="username or email"
placeholderTextColor='whitesmoke'
style={styles.input}
/>
<TextInput
placeholder="password"
secureTextEntry
placeholderTextColor='whitesmoke'
style={styles.input}
/>
<TouchableOpacity style={styles.loginbutton} onPress={() => this.props.navigation.navigate("Grades")}>
<Text style={{
textAlign: 'center',
color: "whitesmoke",
fontWeight: '700',
}}>
Login
</Text>
</TouchableOpacity>
</KeyboardAvoidingView>
</View>
);
}
}
const styles = StyleSheet.create({
loginContainer: {
paddingHorizontal: 9,
backgroundColor: "red"
},
input: {
paddingHorizontal: 10,
marginBottom: 10,
color: '#f1c40f', //sunflower color
backgroundColor: '#3498db',
},
logo: {
width: 231,
height: 231
},
wrapper: {
flex: 1,
alignItems: "center",
justifyContent: "center"
}
});
Here is my app.js.
import React from "react";
import { Font } from "expo";
import { Root } from "./app/router";
import { FontError } from "./app/components/fontError";
export default class App extends React.Component {
state = {
fontLoaded: false
};
async componentDidMount() {
await Font.loadAsync({
Arial: require("./app/resources/Arial.ttf")
});
this.setState({ fontLoaded: true });
}
render() {
if (!this.state.fontLoaded) return <FontError/>;
return <Root />;
}
// ...
}
My root component is router.js, I am using react-navigation.
import React, { Component } from "react";
import { createStackNavigator } from "react-navigation";
import {
Login,
Grades,
} from "./screens";
export const Root = createStackNavigator({
Login: {screen: Login},
Grades: {screen: Grades},
});
Feel free to ask me to append any additional information.
Thanks for your help in advance.
To fix my error I just re-cloned the repo and added back in all the code I wrote.
Thankfully I didn't push this error to the master branch.
This fixed the error.
*note in the process I got rid off react-native-extended-stylesheet, so it might have been cause of error.

undefined is not a function (evaluating 'fetch('apiurl')')

I am using below versions
react-native-router-flux ^3.39.1
react-native 0.44.0
I expecting that will call API which I am using with "fetch"
Have used componentDidMount but it's showing another error
undefined is not an object (evaluating 'this._component.getScrollableNode')
But I am getting below error outputs
Steps to reproduce
Create three scene using router flux (In my case App, Login, Home)
Use ScrollView for creating the Login.js Create a button
using TouchableHighlight after that call the fetch with a function
using onPress like onPress={ () => this.fetchData() }
Below code, I am using for App.js
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
AsyncStorage,
} from 'react-native';
import Login from './components/Login'
import Register from './components/Register'
import Home from './components/Home'
import { Scene, Router, TabBar, Modal, Schema, Actions, Reducer, ActionConst } from 'react-native-router-flux'
const reducerCreate = params=>{
const defaultReducer = Reducer(params);
return (state, action)=>{
console.log("ACTION:", action);
return defaultReducer(state, action);
}
};
export default class App extends Component {
constructor(props, context) {
super(props, context);
this.state = {
logged: false,
loading: true,
};
};
componentWillMount(){
self = this;
AsyncStorage.getItem('token')
.then( (value) =>{
if (value != null){
this.setState({
logged: true,
loading: false,
});
}
else {
this.setState({
loading: false,
})
}
});
};
render() {
if (this.state.loading) {
return <View><Text>Loading</Text></View>;
}
return (
<Router>
<Scene hideNavBar={true} key="root">
<Scene key="logIn" component={Login} title="Login" initial={!this.state.logged}/>
<Scene key="regisTer" component={Register} title="Register"/>
<Scene key="home" component={Home} title="home" initial={this.state.logged}/>
</Scene>
</Router>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
And below code, using for Login.js
/* #flow */
import React, { Component } from 'react';
import {
View,
StyleSheet,
Image,
ScrollView,
TextInput,
Text,
TouchableHighlight,
Alert,
} from 'react-native';
import { Container, Content, InputGroup, Input, Icon, Item } from 'native-base';
import Button from 'react-native-button'
import {Actions} from 'react-native-router-flux'
import ResponsiveImage from 'react-native-responsive-image'
export default class Login extends Component {
constructor(props){
super(props)
this.state = {
email: '',
password: '',
data: '',
}
}
fetchData() {
fetch('http://allstariq.tbltechnerds.com/api/login/?username=andress&password=23434')
.then((response) => response.json())
.then((responseData) => {
this.setState({
data: responseData.movies,
});
})
.done();
}
render() {
return (
<View style={styles.container}>
<ScrollView>
<View style={ styles.logoContainer }>
<View style={{flexDirection: 'row',}}>
<ResponsiveImage
source={require('../assets/logo.png')}
initWidth="300"
initHeight="160" />
</View>
</View>
<View style={ styles.formContainer }>
<Item>
<Icon active name='mail' />
<Input
onChangeText={(text) => this.setState({email: text})}
value={this.state.email}
placeholder='Email'/>
</Item>
<Item>
<Icon active name='key' />
<Input
onChangeText={(text) => this.setState({password: text})}
value={this.state.password}
placeholder='Password'/>
</Item>
<TouchableHighlight
style={ styles.loginButton }
onPress={ () => this.fetchData() }>
<Text style={ styles.btnText}>Login</Text>
</TouchableHighlight>
</View>
<View style={ styles.bottomContainer }>
<Text style={ styles.cenText }>Dont worry if you haven't an account yet . . </Text>
<Text
style={ styles.blueText}
onPress={ Actions.regisTer }
>Register Now</Text>
</View>
</ScrollView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
logoContainer: {
flex: .5,
padding: 10,
justifyContent: 'center',
alignItems: 'center',
},
logoItem: {
width: null,
height: null,
resizeMode: 'cover',
},
formContainer: {
flex: 4,
padding: 10,
},
inputelm: {
marginBottom: 10,
backgroundColor: '#999',
borderWidth: 0,
fontSize: 20,
color: '#FFF',
fontFamily: 'AmaticSC-Bold',
},
loginButton: {
borderRadius: 3,
marginBottom: 20,
marginTop: 20,
paddingLeft: 10,
paddingRight: 10,
backgroundColor: '#2196f3',
elevation: 4,
},
signupButton: {
borderRadius: 3,
marginBottom: 20,
marginTop: 20,
paddingLeft: 10,
paddingRight: 10,
backgroundColor: '#7cb342',
elevation: 4,
},
btnText: {
textAlign: 'center',
color: '#FFF',
fontSize: 30,
lineHeight: 40,
},
blueText: {
textAlign: 'center',
color: '#2196f3',
fontSize: 20,
lineHeight: 40,
},
bottomContainer: {
flex: 1,
padding: 10,
},
cenText: {
textAlign: 'center',
fontSize: 16,
},
});
What is the actual way to use fetch with react-native-router-flux? I am new to react, please help me.
well, its a couple of months late but the problem here is that your fetchData method hasn't got access to this because when you declare a method like this:
fetchData() {
}
Under the hood react is creating a function this way:
this.fetchData = function() {
}
when you declare a function using the function keyword, everything between {} will have its own "this" and your method will not have access to the "this" of the component context.
That is the reason why you are getting the "undefined", because you are calling "this.setState" inside the promise returned by fetch, so the error is not fetch, but this.
To solve this issue you could just define your method this way:
fetchData = () => {
}
And because the functions defined with a fat arrow do not create their own scope, the component "this" will be available from inside your method.
Did you try maybe to import the library?
Import fetch from "fetch";
You have not imported the fetch function, import it from the node-fetch module like
import fetch from 'node-fetch'

Resources