Why react-router-native does not render my component? - react-native

Trying to implement a protectedRoute utils for react-native project, which basicly looks for JWT. First it shows loading indicator, and if there is no JWT present it would redirect to /login.
const LoadingComponent = () => (
<View>
<ActivityIndicator/>
</View>
)
class PrivateRoute extends React.Component {
state = {
loading: true,
jwt: null,
}
componentDidMount() {
storage.retrieve('JWT').then(jwt => this.setState({ loading: false, jwt }))
}
render() {
const { children } = this.props;
const { jwt, loading } = this.state;
if (loading) {
return <Route {...children} component={LoadingComponent}/>
}
if (!jwt) {
return <Redirect to="/signup" />;
}
return <Route {...children} />
}
}
export default PrivateRoute;
this.props.children has all the required information to make a Route in the application. The original idea is that in case of loading we would just overwrite this.props.children.component with custom loading screen.
BUT the solution which does not give me error is (only warning):
if (loading) {
return LoadingComponent
}
I have tried also to manually inline component as
component={() => <View>{...}</View>
render={() => ...}
However it also ends up as the same error. Invariant Violation: Element type is invalid: expected a string ..... but got: undefined

Related

React Native: Getting data from Firebase

I'm simply trying to retrieve data from the database in Firebase, and here's what I've got
var userList = [];
firebase.database()
.ref('/users/')
.once('value')
.then(snapshot => {
snapshot.forEach((doc) => {
userList.push(doc.val());
});
});
console.log(userList);
Even though I copy and pasted this code from a tutorial, the userList is empty outside of the snapshot. Can you tell me why that is?
The request to firebase is asynchronous so console.log(userList); is called before userList.push(doc.val()); gets called.
You should make userList a component state variable so that when you update it your component will re render.
Something like the following should work:
class UserListComponent extends Component {
constructor(props) {
super(props);
this.state = {
userList: [],
};
}
componentDidMount() {
this.getUsers();
}
getUsers() {
firebase
.database()
.ref('/users/')
.once('value')
.then((snapshot) => {
snapshot.forEach((doc) => {
this.setState({
userList: [...this.state.userList, doc.val()],
});
});
});
}
render() {
return (
<View>
{this.state.userList.map((item) => {
return (
<View>
<Text>{item.name}</Text>
</View>
);
})}
</View>
);
}
}

Why the console log doesn't appear in my function?

I'm trying to see the console log of this state inside the function "RenderX()", but nothing happens. When I tried in another function, like in render function or inside the componentWillMount, display normally. Anyone has seen this problem before?
export default class CapaMagazine extends Component {
state = { capa: [this.props.capa.edicaoID] };
componentWillMount() {
Firebase.name
const db = firebase.firestore();
db.collection('revista_capa').get().then((result) => {
result.forEach((doc) => {
if (this.state, '==', doc.data().edicaoID) {
this.setState({ capa: doc.data() })
}
});
});
}
funcaoX(){
console.log(this.state);
}
render() {
return (
<Container>
<Content>
</Content>
</Container>
);
}
}

React Native - Component update parent

I'm making an app in react native and I'm facing a little problem.
I finished the first layout and now I want to change the style all over the app with a second layout
This is what I have in my parent.
As you can see I use AsyncStorage to check when you open again the app the last selected layout. It all working perfectly.
export default class Home extends React.Component
{
constructor(props){
super(props);
this.state = {
view:0
}
}
componentWillMount()
{
this.checkStructureView();
}
checkStructureView = async() =>
{
const StructureView = await
AsyncStorage.getItem('#StructureView');
if(StructureView == 1)
{
this.setState({
view:1
})
}
else
{
this.setState({
view:0
})
}
}
render()
{
if(this.state.view == 1)
{
return(
<ChangeView/>
...
)
}
else
{
return(
<ChangeView/>
...
)
}
}
}
And this is my component ChangeView. It's a little bit messy because I have for each button active/inactive styles. This is also working perfectly, but the problem is that when I click on the button to change the layout will not change it, only after I refresh the app.
First I added this inside the parent and after I updated the state, the layout has changed instantly but I have more pages where I need to add this component, that's why I'm using an component.
So my question is how can I update instantly the parent state so my layout changes every time I click on the component button without reloading the app.
import React, { Component } from 'react'
import {
View,
Text,
Image,
TouchableOpacity,
AsyncStorage
} from 'react-native'
export default class ChangeView extends Component {
constructor(props){
super(props);
this.state = {
position: this.props.position,
view:0,
view1:require(`../assets/icons/view1_inactive.png`),
view2:require(`../assets/icons/view2_active.png`)
}
}
componentDidMount()
{
this.checkViewStructure();
}
checkViewStructure = async()=>
{
const StructureView = await AsyncStorage.getItem('#StructureView');
if(StructureView == '0')
{
this.setState({
view1:require(`../assets/icons/view1_inactive.png`),
view2:require(`../assets/icons/view2_active.png`)
})
}
else
{
this.setState({
view1:require(`../assets/icons/view1_active.png`),
view2:require(`../assets/icons/view2_inactive.png`)
})
}
}
changeToList = async() =>
{
const StructureView = await AsyncStorage.getItem('#StructureView');
if(StructureView == '0')
{
await AsyncStorage
.setItem('#StructureView', '1')
.then( () => {
//
})
.catch( () => {
alert('Something happened! Please try again later.');
});
this.setState({
view1:require(`../assets/icons/view1_active.png`),
view2:require(`../assets/icons/view2_inactive.png`)
})
}
}
changeToPics = async() =>
{
const StructureView = await AsyncStorage.getItem('#StructureView');
if(StructureView == '1')
{
await AsyncStorage
.setItem('#StructureView', '0')
.then( () => {
//
})
.catch( () => {
alert('Something happened! Please try again later.');
});
this.setState({
view1:require(`../assets/icons/view1_inactive.png`),
view2:require(`../assets/icons/view2_active.png`)
})
}
}
render()
{
if(this.state.position === 0)
return(
<View style={{alignItems:'flex-end',marginTop:20,marginBottom:10,justifyContent:'flex-end',flexDirection:'row'}}>
<View>
<TouchableOpacity
onPress= {() => this.changeToList()}
>
<Image
source={this.state.view1}
style={{width:15,height:21,margin:5}}
/>
</TouchableOpacity>
</View>
<View>
<TouchableOpacity
onPress= {() => this.changeToPics()}
>
<Image
source={this.state.view2}
style={{width:15,height:21,margin:5}}
/>
</TouchableOpacity>
</View>
</View>
)
else
return null
}
}
The ChangeView component only changes state in that specific component. There are several ways of propagating change to the parent component. One way is to implement an onChange prop for the ChangeView component. Your Home component render function would then look like something like this:
render() {
if(this.state.view == 1) {
return(
<ChangeView onChange={ (view) => this.setState({ view }) } />
...
)
} else {
return(
<ChangeView onChange={ (view) => this.setState({ view }) } />
...
)
}
}
You can read more about props here: https://reactjs.org/docs/typechecking-with-proptypes.html
There are other ways of doing this if you have state handler for your application such as Redux.

Can I loop componentWillMount until I get the user_key from API?

I am trying to use react navigation authentication flow to manage the login screen if the user is logged in or not. But now I got stuck in AsyncStorage. So while the user is not logged in I presume that componentWillMount will wait until the user will input the credentials, tap the login button, receive the user_id from API call and then try again. For me now it is calling what in the beginning which is fine but then I have to exit from app and go back to get the dashboard rendered. Any solution?
This is my code from App.js where I'm creating the routes as well. Also I am loading redux map on bottom.
export const createRootNavigator = (signedIn = false) => {
return SwitchNavigator(
{
SignedIn: {
screen: SignedIn
},
SignedOut: {
screen: SignedOut
}
},
{
initialRouteName: signedIn ? "SignedIn" : "SignedOut"
}
);
};
class App extends Component {
constructor(props) {
super(props);
this.state = {
signedIn: false,
checkedSignIn: false
};
}
async componentWillMount() {
await isSignedIn()
.then(res => this.setState({ signedIn: res, checkedSignIn: true }))
.catch(err => alert("An error occurred"));
}
render() {
const { checkedSignIn, signedIn } = this.state;
// If we haven't checked AsyncStorage yet, don't render anything (better ways to do this)
if (!checkedSignIn) {
return null;
}
const Layout = createRootNavigator(signedIn);
return (
<SafeAreaView style={styles.safeArea}>
<View style={{flex: 1, backgroundColor: '#ffffff'}}>
<StatusBar barStyle="light-content"/>
<Layout />
<AlertContainer/>
</View>
</SafeAreaView>
)
}
};
And here is the Auth.js where I am waiting for the user_key.
export let USER_KEY = 'myKey';
export const onSignIn = async () => { await AsyncStorage.setItem(USER_KEY, 'true') };
export const onSignOut = async () => { await AsyncStorage.removeItem(USER_KEY) };
export const isSignedIn = () => {
return new Promise((resolve, reject) => {
AsyncStorage.getItem(USER_KEY)
.then(res => {
if (res !== null) {
// console.log('true')
resolve(true);
} else {
resolve(false);
// console.log('false')
}
})
.catch(err => reject(err));
});
};
A solution would be to make use of Splashscreen. You can add a splashscreen to the App. While Splashscreen is being displayed, check if user exists in Asyncstorage, if they do, navigate user to the Dashboard/Homescreen and if asynstorage responds null, navigate user to the Login page. Once Navigation is complete, you can hide the splashscreen. Checkout this package in npmjs for Splashscreen setup react-native-splash-screen

Implement FB login with react native and redux

I want to use Redux framework in my react native based app for implementing Facebook login (I am learning Redux at the moment). I am looking for suggestions on how to structure my Facebook login code to use the redux. More specifically, what actions, reducer and store should I create?
Below is the current Facebook based login code that I have in my app (it does not use redux structure). I have deleted the unrelated code to keep things simple:
index.ios.js
class ProjectXApp extends React.Component {
constructor(props) {
// Set the use to NULL
this.state = {
user: null,
};
}
handleLogin(user) {
this.setState({
// Update the user state once the login is complete
user,
});
}
renderScene(route, navigator) {
const Component = route.component;
return (
<View style={styles.app}>
<Component
user={this.state.user}
navigator={navigator}
route={route}
/>
</View>
);
}
render() {
return (
<Navigator
renderScene={this.renderScene.bind(this)}
initialRoute={{
// Render the Login page in the beginning
component: Login,
props: {
onLogin: this.handleLogin.bind(this),
},
}}
/>
);
}
}
Login.js
// Import Facebook Login Util Component
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
// 'false' means responseToken is not required. 'true' means responseToken is required
responseToken: false,
};
}
// This method gets the fb access token, if the token is returned then
// I render the Main App component (switchToMain method). If the
// access token is not returned then I render a login Button (Refer to render method)
async getAccessToken() {
let _this = this;
await (FBSDKAccessToken.getCurrentAccessToken((token) => {
if(!token) {
_this.setState({responseToken: true})
return;
}
_this.setState({responseToken: true});
_this.props.route.props.onLogin({user: true});
_this.switchToMain();
}));
}
switchToMain() {
this.props.navigator.push({
component: Main, // Render the app
props: {
onLogOut: this.onLogOut.bind(this)
}
});
}
componentDidMount() {
this.getAccessToken();
}
onLoginButtonPress() {
// Shows transition between login and Main Screen
this.setState({responseToken: false})
FBSDKLoginManager.logInWithReadPermissions(['public_profile','email','user_friends'], (error, result) => {
if (error) {
alert('Error logging in');
} else {
if (result.isCancelled) {
alert('Login cancelled');
} else {
this.setState({result});
this.getAccessToken();
}
}
});
}
onLogOut() {
this.setState({responseToken: true});
}
render() {
// This component renders when I am calling getAccessToken method
if(!this.state.responseToken) {
return (
<Text></Text>
);
}
// This renders when access token is not available after calling getAccessToken
return (
<View style={styles.container}>
<TouchableHighlight
onPress={this.onLoginButtonPress.bind(this)}
>
<View>
// Login Button
</View>
</TouchableHighlight>
</View>
);
}
}
// Removed the styling code
Logout.js
import { FBSDKLoginManager } from 'react-native-fbsdklogin';
class Logout extends React.Component {
onLogOut() {
FBSDKLoginManager.logOut();
this.props.onLogOut();
this.props.navigator.popToTop();
}
render() {
return (
<View>
<TouchableHighlight
onPress={this.onLogOut.bind(this)}
>
<View
// Styles to create Logout button
</View>
</TouchableHighlight>
</View>
);
}
});
// Removed the styling code
Have you looked at this lib:
https://github.com/lynndylanhurley/redux-auth?