Auth0 is not passing user props with react.js class components - reactjs

I have multiple routes in my app and I have added Auth0 in Header.js (a child of App.js) to be able to keep the user logged-in amongst all the routes. I was also able to get Auth0 user prop object (name, email, and picture) to create accounts for the users in my database.
Now in another child component of App.js (Feed.js), I'm trying to use the email from Auth0 props as a query parameter to send a GET request to the database, but each time I do so I either receive an error (user is not found) or undefined.
Feed.js code looks like this:
import React, { Component } from 'react'
import axios from "axios";
import { withAuth0 ,auth0} from '#auth0/auth0-react';
class Feed extends Component {
constructor(props){
super(props);
this.state={
userdata:[]
}
}
getdata = async() => {
await axios
.get(`http://localhost:1177/getuser?email=this.props.auth0.user.email`)
.then((res) => {
this.setState({
userdata: res.data,
});
});
};
render() {
this.getdata();
return (
<div>
<img src={this.state.userdata.pp} alt={this.state.userdata.username} />
<h1>{this.state.userdata.username}</h1>
<h1>{this.state.userdata.email}</h1>
</div>
)
}
}
export default withAuth0(Feed)
So any ideas on how to make this work?

You might have an error because the expression used in the template literal is not correctly typed between a dollar sign and curly braces in the GET request.
Try this:
`http://localhost:1177/getuser?email=${this.props.auth0.user.email}`;

Related

axios.get ERROR on using axios.create method by using baseUrl

I tried to create an application from an Random user API in react and I used axios library for HTTP Requests.I created a separate file for base API using axios.create and the file code goes as,
import axios from 'axios'
export default axios.create({
baseURL: `http://jsonplaceholder.typicode.com`,
});
Then I used this in another file to make an GET request and store the data in state on componentdidMount so ill can access the data in UI.
import React from "react";
import API from "../api";
export default class PersonList extends React.Component {
state = {
persons: []
};
componentDidMount() {
API
.get('/').then((data) => {
const persons = data.data;
this.setState({ persons });
console.log(this.state.persons);
});
}
render() {
const { persons } = this.state;
console.log('Stato',persons)
return (
<ul>
{persons.map((person) => (
<li key={person.id}>{person.name}</li>
))}
</ul>
);
}
}
But it doesn't work ,because the state is not filled up with Users data,so the .map() function is throwing an error.
You're encountering this error because of the URL you are using. In your example, you use https://jsonplaceholder.typicode.com as the endpoint in componentDidMount, but that isn't going to return any placeholder user data. I believe you meant to use https://jsonplaceholder.typicode.com/users instead.
I have a working example here: https://codesandbox.io/s/axios-instance-ki9g6. Notice how I only had to change / in componentDidMount to /users.

Is it possible to trigger function after logging in by withAuthenticator() of AWS Amplify?

I would like to trigger function when user login by withAuthenticator() of Amplify.
(I need to send state data to other component by contextAPI of React.js)
I've found explanation of SignIn() function, but I didn't find something about function when users login
▼Main.tsx (Main page, everyone can watch)
import React from 'react';
import { BrowserRouter as Router, Route } from "react-router-dom";
import Login from './Login';
import Mypage from './mypage/Index';
import Menu from './Menu';
import Hoge from './Hoge';
class Main extends React.Component {
render(){
return (
<div className="Main">
<Router>
<Menu />
//**Mypage ← only logged in can watch
<Route exact path="/mypage" component={Mypage} />
<Route path="/main" component={Hoge} />
</Router>
</div>
);
}
}
export default Main;
▼mypage.tsx (looged in users can watch)
import React from 'react';
import { RouteComponentProps, Link } from 'react-router-dom';
import { withAuthenticator } from 'aws-amplify-react';
import Auth from '#aws-amplify/auth';
import AuthContext from '../context/auth-context';
interface MatchParams {
id: number;
}
interface State {
user: '',
}
class Mypage extends React.Component<RouteComponentProps<MatchParams>, State> {
constructor(props: RouteComponentProps) {
super(props);
this.state = { user: '' };
}
async componentDidMount() {
let user = await Auth.currentAuthenticatedUser()
this.setState({user: user.username});
}
//**(it's just what I want now)
//loggedIn() {
// console.log("logged in!");
//}
render() {
return (
<div className="mypage">
<h1>mypage</h1>
<div>
<ul>
<li><Link to='hoge'>hoge</Link></li>
</ul>
</div>
</div>
);
}
}
export default withAuthenticator(Mypage);
Answer to J.Hesters
・your ideas 1,2
actually I thought about these ideas(creating signIn form myself), but I wanted to know how it works without them (i should have write about it at first time)
・your idea 3
I inserted console.log("login in!!") in componentDidUpate() but it didnt work after login
Maybe I gotta use the way 1 or 2, but if you get why it doesnt work ur idea3, plz write it here Anyway thx for answering sir ^^
I may be late to answer but you should be using Amplify Logger.
You can then launch whatever code when you need to based on when the event happens. I would do something like this.
import { Hub, Logger } from 'aws-amplify';
const logger = new Logger('Logger', 'INFO');
const listener = (data) => {
switch (data.payload.event) {
case 'signIn':
logger.info('user signed in');
break;
case 'signUp':
logger.info('user signed up');
break;
case 'signOut':
logger.info('user signed out');
break;
case 'signIn_failure':
logger.info('user sign in failed');
break;
case 'configured':
logger.info('the Auth module is configured');
break;
default:
logger.error('Something went wrong, look at data object', data);
}
}
Hub.listen('auth', listener);
You can just write whatever code you want to execute after the await keyword.
async componentDidMount() {
let user = await Auth.currentAuthenticatedUser();
this.setState({user: user.username});
console.log('logged in.');
}
Edit:
As far as I know, you can't explicitly overwrite withAuthenticator's methods. So you have three options as far as I'm concerned:
Supply a custom <SignIn /> component to with authenticator in which you handle the login process manually and invoke whatever function you like as soon as the login method finishes.
Write the whole UI login UI yourself and use Amplify's Auth methods explicitly whenever you need to. Here is an example repository doing it.
Use componentDidUpdate() to trigger code after the component rerenders when the user logs in. Be careful to not create infinite loops with setState. componentDidUpdate only gets called, when the component rerenders. Components within withAuthenticator don't neccesarily rerender.

Props only showing under inline if statement in react redux

Been fooling around with some code and came into something tricky. Currently I am showing user data if the user isAuthenticated. However, I have to put this.props.user ? in the inline statement. Otherwise, this.props.user comes back as undefined, although, with this.props.user ? it works.
Here is the code
// import React from "react";
// import { connect } from "react-redux";
// import { getUser } from "../store/actions/userActions";
// import { withRouter } from 'react-router-dom';
import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom'
import * as actions from '../store/actions/auth'
class UserDetailView extends React.Component {
componentDidMount(){
this.props.onTryAutoSignup()
this.props.getfetchUser(this.props.username)
console.log(this.props)
const user = this.props.user
}
render(){
return(
<div>
{
this.props.user//need this or the below become undefined && this.props.isAuthenticated ?
<div>
Welcome {this.props.user.username} {this.props.user.email}
</div>
:
<div><Link to='/login/'>Login</Link></div>
}
</div>
)
}
}
const mapStateToProps = state => {
return{
isAuthenticated: state.token !== null,
user: state.user,
username: state.username
}
}
const mapStateToDispatch = (dispatch) => ({
logout: () => dispatch(actions.logout()),
onTryAutoSignup: () => dispatch(actions.authCheckState()),
getfetchUser: id => dispatch( actions.fetchUser( id ) )
})
export default connect(mapStateToProps, mapStateToDispatch)(UserDetailView);
That is simply because on the "first load" of the component the user data is not valid (I assume, I can't see your reducers)
So let's say your reducer sets the default user as null
now this component is loaded
componentDidMount dispatches and calls getfetchUser
getfetchUser will take some time...
the component render is not waiting so it will call render method
but user is not set yet as you didn't get the response from the server
so on the first render, you see user as null
but as soon as ajax call returns it sets the user and you are good
so that's why you see this "weird" behavior, but that's how it is in the current implementation.
You can try different tricks to make sure you are good, like:
stick to what you did
add a loading flag, so you could know you are waiting for a response from the server. This is the better option because you could handle errors from the server.
You need a check because you are accessing the username and email attributes of the user object.
Assuming on initial render,
user = {} // this wont have properties initially
isAuthenticated = true
You try to access the email and username, which don't exist at that point of time.
To avoid this,
Maybe you can pass the isAuthenticated inside your user object itself. This will maintain integrity.

React not passing props to children?

I'm trying to pass the data from this axios call into a child component, Hero. Despite having passed down the props and made a successful axios call it won't actually make it into the Hero div.
When I console.log on the child component it claims to have the data but then fails to push it to the champions array so I can't use it. Any ideas?
Edit:
I'll add in here that I do have react-router installed in this project however this data is being passed around across one "view" and not multiple pages.
This is the parent component
import React, { Component } from 'react';
import axios from 'axios';
import './assets/stylesheets/screen.css';
import Hero from './Hero';
import Info from './Info';
class Home extends Component {
constructor(props){
super(props);
this.state = { champions: [] };
}
componentDidMount() {
axios.get(
'https://api.pinterest.com/v1/boards/gasulliv/pose-
references/pins/?access_token=AQjW6hDdAF0egwEesZA6oJbqP0XQFQ-
m6_jg2RpErKPqdSA7cQAAAAA&limit=100&fields=id%2Clink%2Cnote%2
Curl%2Coriginal_link%2Cimage').then(champions => {
this.setState({ champions });
console.log(champions);
});
}
render() {
return (
<div>
<Hero champions = {this.state.champions} />
<Info />
</div>
);
}
}
export default Home;
And this is child component (at this console log I get two answers, one claiming it has the data and another claiming it does not):
import React from 'react';
import Header from './Header';
import 'bootstrap/dist/css/bootstrap.min.css';
import './assets/stylesheets/screen.css';
const Hero = (props) => {
console.log(props);
return (
<div className = "jumbotron kindred">
<Header />
<div className = "textHolder">{ props.champions.length }</div>
</div>
)
}
export default Hero;
You have to access the data in the data key response.data
Try the following.
axios.get('https://api.pinterest.com/v1/boards/gasulliv/pose-references/pins/?access_token=AQjW6hDdAF0egwEesZA6oJbqP0XQFQ-m6_jg2RpErKPqdSA7cQAAAAA&limit=100&fields=id%2Clink%2Cnote%2Curl%2Coriginal_link%2Cimage')
.then((response) => {
this.setState({
champions: response.data
})
})
.catch((error) => {
// Do something with the error
})
Thanks for help but it turns out the issue had to do with the fact that I had the router installed. Likely I just need to pass that data around through the router instead of the pages.
Kudos for the help!

Login in React.js with localStorage token

Im developing a Login system of a React WebSite. We're using api_tokens for access to the API and retrieve the info as validate user credentials. I need to restringe everything when the auth fails and redirect to the user login page, currently Im using redux as app global state. The issue is I'm saving the api_token in browser localStorage, and I need to dispatch the UNAUTH_USER when the api_token is modified by Browser Clear History or other things........
I was wondering if I can attach some eventListener to that... and if is the right solution..
The code looks below:
import React from 'react'
import Header from './Header'
import Navigation from '../navigation/components/Navigation'
import Ribbon from '../ribbon/Ribbon'
import Footer from './Footer'
import Shortcut from '../navigation/components/Shortcut'
import LayoutSwitcher from '../layout/components/LayoutSwitcher'
import {connect} from 'react-redux'
// require('../../smartadmin/components/less/components.less');
class Layout extends React.Component {
constructor(props) {
super(props);
}
componentWillMount() {
if(!this.props.authenticated) {
this.props.router.push('/login');
}
}
componentWillUpdate(nextProps) {
if(!nextProps.authenticated) {
this.props.router.push('/login');
}
}
render() {
if (!this.props.authenticated) {
this.props.router.push('/login');
return null;
}
else {
return (
<div>
<Header />
<Navigation />
<div id="main" role="main">
<LayoutSwitcher />
<Ribbon />
{this.props.children}
</div>
<Footer />
<Shortcut />
</div>
)
}
}
}
const mapStateToProps = (state) => {
return {
authenticated: state.auth.authenticated
};
}
export default connect(mapStateToProps)(Layout);
I do the same thing in my app. For every call my Sagas make, if an api error occurs and a 403 expired/unvalid token is raised, I dispatch there a redux action that clears localstorage and disconnect the user (I have a refresh token mechanism retry before that).
I'm not sure that you can attach an eventListener to a localStorage expiration, but placing a check at your API calls mechanism would be IMO a good practice.
Best

Resources