I have a problem with react-native, I am trying to login with facebook using the expo-facebook and firebase libraries, it loads everything well and it starts session well, but when it loads the following screen the following comes up:
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://facebook.com/react-invalid-hook-call for tips about how to debug and fix this problem.
Code:
import React, { Component } from 'react'
import { View, Button, Text, StatusBar, LayoutAnimation, Image, StyleSheet } from 'react-native'
import { Toast } from 'native-base'
import * as Facebook from 'expo-facebook'
import * as firebase from 'firebase'
const app = {
id: APP_ID,
name: APP_NAME,
}
export default class LoginScreen extends Component {
static navigationOptions = {
header: null,
}
facebook = async () => {
Facebook.initializeAsync(app.id, app.name)
const { type, token } = await Facebook.logInWithReadPermissionsAsync(app.id, {
permission: 'public_profile',
})
if ( type == "success" ) {
const credential = firebase.auth.FacebookAuthProvider.credential( token )
firebase.auth().signInWithCredential(credential)
.catch(e => {
console.log(e)
})
}
this.props.navigation.navigate('App')
}
render() {
LayoutAnimation.easeInEaseOut()
return <View>
<Button title="Log in with facebook" style={styles.button} onPress={this.facebook}>
</Button>
</View>
}
}
Here is an image of error:
Related
I am trying to implement Google Authentication into my Expo/react native component based class, but the example Expo gives is for functional components.
They use this code for the Google Authentication:
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import * as Google from 'expo-auth-session/providers/google';
import { Button } from 'react-native';
WebBrowser.maybeCompleteAuthSession();
export default function App() {
const [request, response, promptAsync] = Google.useAuthRequest({
expoClientId: 'GOOGLE_GUID.apps.googleusercontent.com',
iosClientId: 'GOOGLE_GUID.apps.googleusercontent.com',
androidClientId: 'GOOGLE_GUID.apps.googleusercontent.com',
webClientId: 'GOOGLE_GUID.apps.googleusercontent.com',
});
React.useEffect(() => {
if (response?.type === 'success') {
const { authentication } = response;
}
}, [response]);
return (
<Button
disabled={!request}
title="Login"
onPress={() => {
promptAsync();
}}
/>
);
}
My question is how would I go about doing something similar to this for a class based component (export default class App extends Component)
This is actually a general subject, how to call React's functional components from within class components.
Specific to Google Sign In, this answer can help: https://stackoverflow.com/a/66974167/1870873
I'm trying to use the next.js with authentication for a small project. The authentication currently works but doesn't allow me to show the data in my navbar.
I was using it with firebase originally BUT NOT ANYMORE!! Now have the authentication set up separately below.
This is the example repo, it has my API in it for auth and the next.js, which i'm trying to integrate together to have login and logout working with header's set for api calls.
https://github.com/Hewlbern/example
Just getting the basic login and logout functionality, so I can control user access to my website. I know this is really simple - just quite confused how to do it with next.js with how document page an app works :S
I am trying to show a table of output from this API, and give the ability to download the outputed json (into a CSV or whatever). So having that available after a search with the query params, and only on a page after the user is logged in, is the point :)
Here's an example of the login functionality I'm using.
import { useRef, useState } from 'react';
import React from 'react'
import PropTypes from 'prop-types'
import Layout from "../components/Layout";
export default function Login() {
const emailRef = useRef<HTMLInputElement>(null);
const passRef = useRef<HTMLInputElement>(null);
const [message, setMessage] = useState<any>(null);
async function handleLogin() {
const resp = await fetch('http://localhost:3001/auth/login', {
method: 'POST',
headers: {
'Content-Type': "application/x-www-form-urlencoded"
},
body: JSON.stringify({
email: emailRef.current?.value,
password: passRef.current?.value
})
});
const json = await resp.json();
setMessage(json);
}
return (
<Layout>
{JSON.stringify(message)}
<input type="text" placeholder="email" ref={emailRef} />
<input type="password" placeholder="password" ref={passRef} />
<button onClick={handleLogin}>Login</button>
</Layout>
);
}
This is posting to this api request
router.post('/login', (req, res) => {
// console.log(req.body)
let email = req.body.email;
let password = req.body.password;
console.log(email,password)
DatabaseService.GetUser(email).then(user => {
if(user===null){
res.sendStatus(404);
}
else{
if(bcrypt.compareSync(password, user[0].password)) {
jwt.sign({user}, 'secretkey', { expiresIn: '30d' }, (err, token) => {
DatabaseService.SetSession(token,JSON.stringify(user[0].user_id)).then(inserted=>{
res.json({
token
});
});
});
} else {
res.sendStatus(500);
}
}
});
});
So just with this small example, hat's wrong with how I'm sending the requests currently? (thinking it's the format the login takes requests in?)
If someone has done something similar or knows how to solve these issues, I'd really appreciate it :)
Cheers!
What I'd recommend here is to create a custom hook which makes use of React's Context API in order to "monitor" the auth state changing. Then wrap you app in that provider and you'll have the flexibility do anything you want with that auth state using your new custom hook.
Here's an example of how that custom hook would look using a authentication with Firebase:
import React, { createContext, useContext, useState } from 'react'
import { auth } from './services' // this is just firebase.auth()
const UserContext = createContext()
export const UserProvider = ({ children }) => {
const [user, setUser] = useState(undefined)
auth.onAuthStateChanged(setUser)
return <UserContext.Provider value={user}>{children}</UserContext.Provider>
}
export const useUser = () => useContext(UserContext)
Now you just need to wrap your app in the UserProvider.
Like this:
import React, { StrictMode } from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import App from './app'
import { UserProvider } from './hooks'
const rootElement = document.getElementById('root')
ReactDOM.render(
<StrictMode>
<BrowserRouter>
<UserProvider>
<App />
</UserProvider>
</BrowserRouter>
</StrictMode>,
rootElement
)
Then as an example, let's say you wanted to automatically direct away from your Login page if the use is logged it. You could make use of the useEffect hook, and useHistory hooks to navigate to / if the user is logged in.
Something like this will do:
import React, { useEffect } from 'react'
import { useHistory } from 'react-router-dom'
import { useUser } from './hooks'
const LoginPage = () => {
const history = useHistory()
const user = useUser() // user is undefined if not logged in
useEffect(() => {
if (user) { // will run the condition if user exists
history.push('/')
}
}, [user])
...
}
You could go on to actually use the user data in your navigation bar using something like this:
import React from 'react'
import { Link } from 'react-router-dom'
import { useUser } from './hooks'
const NavBar = () => {
const user = useUser()
return (
<div>
{user ?
<Link to="/profile">Welcome, {user.displayName}</Link> :
<Link to="/login">Login</Link>
}
</div>
)
}
Obviously you can change this for us according to your own needs, but all this should get you going with how work with authentication state in a clean robust manner.
I am trying to use react with firebase to make an app that allows all users to read everything and some users to write to some functions.
At the moment, I have my firestore security rules set to allow all users to read and write everything, and then I have a withAuthorisation wrapper that I want to put around a component that contains a link to make write a new document. I know this isn't secure, but I'm just trying to get the hang of how to separate the code so that I can build view layers that render the content in line with the permissions I write.
At the moment I have a references list, which is an index of all the references created. At the bottom of that list, I have a component called 'AddReference' which is a link to the form to make a new reference.
The list is not wrapped in my withAuthorisation wrapper. The AddReference component is wrapped.
I am expecting to be logged out and render the list to all users - (everyone can read the index) but a logged out user should not see the AddReference link.
Instead, the entire list is blocked behind an authentication redirect.
My list of all the references has:
import React, { Component } from 'react';
import { Link, withRouter } from 'react-router-dom';
import { compose } from 'recompose';
import { withFirebase } from '../../Firebase/Index';
import * as ROUTES from '../../../constants/Routes';
import { ReferencesList } from './ReferencesList';
import { Layout, Typography, Card, List, Button, Divider } from 'antd';
import {ReferencesForm} from './Form';
import AddReference from './AddReference';
const { Content } = Layout
const { Title, Paragraph, Text } = Typography;
class ReferencesPage extends Component {
render() {
return (
<div>
<Content
style={{
background: '#fff',
padding: 24,
margin: "auto",
minHeight: 280,
width: '90%'
}}
>
<ReferencesList/>
<br/>
<AddReference />
</Content>
</div>
);
}
}
export default ReferencesPage;
My AddReference component has:
import React, { Component } from 'react';
import { Link, withRouter } from 'react-router-dom';
import { compose } from 'recompose';
import { withFirebase } from '../../Firebase/Index';
import * as ROUTES from '../../../constants/Routes';
import { ReferencesList } from './ReferencesList';
import { Layout, Typography, Card, List, Button, Divider } from 'antd';
import {ReferencesForm} from './Form';
import { AuthUserContext, withAuthorization, withEmailVerification } from '../../Session/Index';
const { Content } = Layout
const { Title, Paragraph, Text } = Typography;
const AddReference = () => (
<AuthUserContext.Consumer>
{authUser => (
<div>
<Divider></Divider>
<div style={{
display: "flex",
justifyContent: "center"
}}>
<Link to={ROUTES.REFERENCESFORM}>Add a Reference</Link>
</div>
</div>
)}
</AuthUserContext.Consumer>
);
const condition = authUser => !!authUser;
export default compose(
// withEmailVerification,
withAuthorization(condition),
)(AddReference);
My withAuthorisation wrapper has:
import React from 'react';
import { withRouter } from 'react-router-dom';
import { compose } from 'recompose';
import { withFirebase } from '../Firebase/Index';
import AuthUserContext from './Context';
import * as ROUTES from '../../constants/Routes';
const withAuthorization = condition => Component => {
class WithAuthorization extends React.Component {
// componentDidMount() {
// this.listener =
this.props.firebase.auth.onAuthStateChanged(authUser => {
// if (!condition(authUser)) {
// this.props.history.push(ROUTES.SIGN_IN);
// }
// });
// }
componentDidMount() {
this.listener = this.props.firebase.onAuthUserListener(
authUser => {
if (!condition(authUser)) {
this.props.history.push(ROUTES.SIGN_IN);
}
},
() => this.props.history.push(ROUTES.SIGN_IN),
);
}
componentWillUnmount() {
this.listener();
}
render() {
return (
<AuthUserContext.Consumer>
{authUser =>
condition(authUser) ? <Component {...this.props} /> : null
}
</AuthUserContext.Consumer>
);
}
}
return compose(
withRouter,
withFirebase,
)(WithAuthorization);
};
export default withAuthorization;
Is it possible to have a component that is wrapped inside an authorisation requirement rendered on a page that is not wrapped? I still want to show the content of the list (other than the AddReference component) for users that do not satisfy the authorisation condition.
1> how to implement permissions in an optimize way ?
Answer:
Best practice for React Router user roles (Firebase)
Role-Based Access Control (RBAC) and React Apps.
2> Is it possible to have a component that is wrapped inside an authorisation requirement rendered on a page that is not wrapped? I still want to show the content of the list (other than the AddReference component) for users that do not satisfy the authorisation condition.
Short Answer: Yes, make the page accessible to all users and hide the content of the page which should not be visible to public.
Answer: Well, we need to have a global state ( REDUX, localStorage, etc ) in which we will store user roles ( guest, login etc ) at the time initialization. then when we need to hide an element from component, we just have to check for a value like below
//set state.userRole from globalState in init function.
(this.state.userRole == 'login') ?
<Icon name='md-log-out' style={styles.logoutIcon} button onPress={() =>
Alert.alert(
'Log out',
'Do you want to logout?',
[
{ text: 'Cancel', onPress: () => { return null } },
{
text: 'Confirm', onPress: () => {this.logout()}
},
],
{ cancelable: false }
)
}/>
:
<View />;
I'm beginner and i try to learn graphql. I want to create an app which update the data(realtime) from react web app to react native mobile app with graphql. In the web app was easy to refetch query when i pressed the OK button. In the mobile app i don't know how to refetch query when i press the button from my web app.
I try to transfer data via websockets(socket.io) and finally i manage to pass the data but it was time-consuming.
Here is WEB APP
and
Here is what i want to do
Web app built with react.js
Mobile app built with react-native.js
Here is my react-native code.I don't know how and where to use refetch query.
App.js
import React, { Component } from "react";
import ApolloClient from "apollo-boost";
import { ApolloProvider } from "react-apollo";
import { View, Text } from "react-native";
import BookList from "./components/BookList";
//apollo client
const client = new ApolloClient({
uri: "http://XXX.XXX.X.X:4000/graphql"
});
class App extends Component {
render() {
return (
<ApolloProvider client={client}>
<View>
<BookList />
</View>
</ApolloProvider>
);
}
}
export default App;
BookList.js
import React, { Component } from "react";
import { graphql } from "react-apollo";
import { Button, View, Text } from "react-native";
import { gql } from "apollo-boost";
import io from "socket.io-client";
const getBooksQuery = gql`
{
books {
name
id
genre
}
}
`;
class BookList extends Component {
displayBooks = () => {
var data = this.props.data;
if (data.loading) {
return <Text>loading books...</Text>;
} else {
return data.books.map(book => {
return <Text>{book.name}</Text>;
});
}
};
render() {
return <View>{this.displayBooks()}</View>;
}
}
export default graphql(getBooksQuery)(BookList);
In the web app it was easy to apply sth that because i put the refetch query inside function which i have when i pressed button like this:
submitForm = e => {
e.preventDefault();
this.props.addBookMutation({
variables: {
name: this.state.name,
genre: this.state.genre,
authorid: this.state.authorid
},
refetchQueries: [{ query: getBooksQuery }]
});
};
*Sorry for my English
So according to the documents of Apollo
You can use Apollo with React Native exactly as you would with React Web.
Here is the corresponding page about implementation with react-native.
https://www.apollographql.com/docs/react/recipes/react-native/
To get the data via sockets in graphQL you need to use subscriptions.
Please follow these steps;
Create a subscription schema and subscription function in your back-end project to be dispatched when a book is updated. I don't know which server lib you use for back-end but I highly encourage you to use apollo-server here.
In your mobile app, you need to subscribeToMore in getBooksQuery which you get all your books.
When a book is updated, your server will send the updated book to the subscribed clients and you will be able to get it from mobile app since you subscribed.
PS: you can use howtographql to learn newer version of apollo. (components which provide render prop functionality)
I'm trying to implement a Sign In to Google from a React-native app. I found this question where someone give a way to do it, but is not working anymore or I'm doing something wrong.
My code is this one:
App.js
"use strict";
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { LoginGoogle } from './src/components/Login.js';
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>Open uuuup App.js ehh to start working on your app!</Text>
<LoginGoogle/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Login.js
"use strict";
import React, { Component } from 'react';
import Expo from 'expo';
import Button from 'react-native';
import { Text, View } from 'react-native';
class LoginGoogle extends Component {
signInWithGoogleAsync = async () => {
try {
const result = await Expo.Google.logInAsync({
androidClientId: process.env.GOOGLE_ANDROID_CLIENT_ID,
iosClientId: process.env.GOOGLE_IOS_CLIENT_ID,
scopes: ['profile'],
})
if (result.type === 'success') {
return result
}
return { cancelled: true }
} catch (e) {
return { error: e }
}
}
onLoginPress = async () => {
const result = await this.signInWithGoogleAsync()
// if there is no result.error or result.cancelled, the user is logged in
// do something with the result
}
render() {
return (<Button onPress={this.onLoginPress}>Login</Button>)
}
}
export default LoginGoogle;
When this is compiled, it says the following error:
Uncaught Error: Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function but got: undefinded. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports
I already found when this error generally happen as states this other question. I already try importing LoginGoogle with and without curly braces, but without luck.
Any idea on what could be happening here? I know that if I put only a Text in LoginGoogle.js and import it from App.js works like a charm, so probably it has something to do with the functions that are related to Google Sign In, but it's a guess, I could be wrong.
You only need to change this line:
import { LoginGoogle } from './src/components/Login.js';
to
import LoginGoogle from './src/components/Login.js';
The first line would work if you exported Login like this:
export {LoginGoogle}
and not
export default LoginGoogle;