I am very confused about this. I am trying to set the isAuthenticated state to true after verifying a JWT, and it won't work. The console logs are outputting weird things (screenshot below).
I suspect I am messing something up with the fetch promise, but I don't know what.
class App extends React.Component {
constructor(){
super()
this.state = {
isAuthenticated: false
}
}
componentWillMount(){
this.authenticate()
}
authenticate(){
fetch("http://localhost:3000/authenticated", {
method: "POST",
headers: {
"Accept": "application/json",
"Content-type": "application/json"
},
body: JSON.stringify({
token: localStorage.getItem("token")
})
})
.then(res => res.json())
.then(data => {
console.log(data); // Server returns true
console.log(this.state); // isAuthenticated: false
console.log(this); // isAuthenticated: true
if(data.success){
this.setState({
isAuthenticated: true
}, console.log(this.state)) // isAuthenticated: false
}
})
.catch(err => {
console.log(err);
});
}
The console logs:
React doesn't bind automatically this to class methods.
You have two ways to handle this.
on constructor
constructor(){
super()
this.state = {
isAuthenticated: false
}
this.authenticate = this.authenticate.bind(this)
}
or a bit cleaner ES6 way and use arrow functions
authenticate = () => {
[your code]
this.setState({ isAuthenticated: true }) // example
}
Related
I am trying to get values from my state and props in function. This function is fired under componentWillReceiveProps method. However when i try to console log my props and state values under that function i get 'undefined' as result. Is there something to do with my state not updating fast enough? Am i missing to pass props somewhere? Where could lay error and how could i get my function to handle correct values?
handleShow() function:
state = {
training: {},
isCreator: null
}
componentWillReceiveProps(newProps) {
console.log(newProps);
if (newProps.token) {
axios.defaults.headers = {
"Content-Type": "application/json",
Authorization: newProps.token
}
const trainingID = this.props.match.params.trainingID;
axios.get(`http://127.0.0.1:8000/api/${trainingID}/`)
.then(res => {
this.setState({
training: res.data
});
console.log('treener', res.data.coach)
})
.catch(err => {console.log(err)})
this.handleShow()
} else{}
}
handleShow() {
if(this.props.token == this.state.training.coach) {
console.log('Props:', this.props.token);// value of undefined
console.log('State.training', this.state.training.coach)//
this.setState({
isCreator: true
})
}else{
this.setState({
isCreator: null
})
}
}
I'm new to react and I have a question about a best practice that sees me make a mistake .
I call an API to retrieve information and modify an array in the state once the response is returned by the API. In the "render" I have to retrieve the information from this array (when it is completed) or it sends me back an error because the array is empty when the render is initialized.
class MyClass extends React.Component {
constructor(props) {
super(props)
this.state = {
activeIndex: 0,
items: []
}
}
componentDidMount() {
axios
.get(`API_ADDRESS`, {
headers: {
Authorization: `Token XXX`,
},
})
.then(function(response) {
this.setState({
items: response.results,
})
})
.catch(error => {
notification.warning({
message: error.code,
description: error.message,
})
})
}
changeDialog = (e, index) => {
e.preventDefault()
this.setState({
activeIndex: index,
})
}
render() {
const { activeIndex, items } = this.state
const {
first_name: firstName,
last_name: lastName,
phone,
email,
address,
} = items[activeIndex]
The error indicates :
TypeError: _items$activeInde is undefined
How can I solve this error related to data loading? (trying to keep the destrying elements method)
Thanks a lot
Eliott
Because API that you fetch from server is async. The first time render of Component, data that you setState in axios still not yet updated, it just updated when Component render the second time.
So you must check state in render Component like this to make sure that if activeIndex is defined then declare variable with items[activeIndex] :
activeIndex && const {
first_name: firstName,
last_name: lastName,
phone,
email,
address,
} = items[activeIndex]
Two issues:
beware of this inside the Promise returned by axios. You use function(){} so the this inside is not the component's instance. Change it to an arrow function.
add a guard so you won't destructure undefined when activeIndex points to an item element that is not there (which happens in the initial loading before the axios fetches the data).
Fix:
// ... (code not shown remains unmodified)
componentDidMount() {
axios
.get(`API_ADDRESS`, {
headers: {
Authorization: `Token XXX`,
},
})
.then(response => { // changed this line
this.setState({
items: response.results,
})
})
// ... (code not shown remains unmodified)
render() {
const { activeIndex, items } = this.state
if (!items[activeIndex]) { // added this line
return <div>Hold tight while items are being fetched...</div>; // added this line
} // added this line
const {
first_name: firstName,
// ... (code not shown remains unmodified)
just change your component like so:
constructor(props) {
super(props)
this.state = {
activeIndex: 0,
items: [],
isFetching: false
}
}
componentDidMount() {
// staring your fetching
this.setState({isFetching: true});
axios
.get(`API_ADDRESS`, {
headers: {
Authorization: `Token XXX`,
},
})
.then(function(response) {
// finish fetching when your response is ready :)
this.setState({
items: response.results,
isFetching: false
});
})
.catch(error => {
// finish fetchnig
this.setState({isFetching: false})
notification.warning({
message: error.code,
description: error.message,
})
})
}
changeDialog = (e, index) => {
e.preventDefault()
this.setState({
activeIndex: index,
})
}
render() {
// if your component is while fetching shows a loading to the user
if(this.state.isFetching) return <div>Loading...</div>;
// if there is no results shows a msg to the user
if(this.state.items.length === 0) return <div>there is not items!!!</div>
const { activeIndex, items } = this.state
const {
first_name: firstName,
last_name: lastName,
phone,
email,
address,
} = items[activeIndex]
I am currently developing an app that requires login/logout. I am using google-login-react (Google OAuth2) to handle most of the signing in heavy work. From there, I am posting the google access token along with some other stuff (id, email, last login, etc..) to my RESTful API. After the user is authenticated, they are redirected to a dashboard. From there, the user can sign out of the application.
What I am having difficulty with is UPDATING the user and the user object after the user signs back in. Right now, every time the user logs back in, it is posting a new object (and thus a new user) to my API. I'm looking to simply just show the updated LAST LOGIN and to store the existing user in the ID they had already been assigned upon initial log in.
Right now, this is what I have thus far:
PostData.js
export function PostData(type, userData) {
let BaseURL = 'https://app.someApp.com/';
return new Promise((resolve, reject) =>{
fetch(BaseURL+type, {
headers:{
"Accept": "application/json",
"Content-Type": "application/json"},
'Access-Control-Allow-Origin':'*',
'Content-Security-Policy':'upgrade-insecure-requests',
method: 'POST',
mode: 'cors',
body: JSON.stringify(userData)
})
.then((response) => response.json())
.then((res) => {
resolve(res);
})
.catch((error) => {
reject(error);
});
});
}
Login.js
class Login extends Component {
constructor(props) {
super(props);
this.state = {
loginError: false,
redirect: false
};
this.signup = this.signup.bind(this);
}
signup(res, type) {
let postData;
var currentTime = new Date();
if (type === 'google' && res.profileObj.email) {
postData = {
email: res.profileObj.email,
realname: res.profileObj.name,
googleId: res.profileObj.googleId,
googleAccessToken: res.Zi.access_token,
googleImageURL: res.profileObj.imageUrl,
lastLogin: currentTime
};
}
if (postData) {
PostData('api/v1/appUsers', postData).then((result) => {
let responseJson = result;
localStorage.setItem("userData", JSON.stringify(responseJson));
this.setState({redirect: true});
});
} else {}
};
render() {
if (this.state.redirect || localStorage.getItem('userData')) {
return (<Redirect to={'/'}/>)
}
const responseGoogle = (response) => {
let isSignedIn = true;
console.log("google console");
console.log(response);
console.log(isSignedIn);
this.signup(response, 'google');
return isSignedIn;
}
Home.js
signOut(e) {
e.preventDefault()
if (localStorage.getItem('userData')) {
localStorage.removeItem('userData');
this.props.history.push('/login')
console.log('shit works');
}
}
constructor(props){
super(props);
this.state = {
name:'',
redirect: false,
};
}
componentDidMount() {
let data = JSON.parse(localStorage.getItem('userData'));
console.log(data);
}
render() {
if(!localStorage.getItem('userData') || this.state.redirect){
return (<Redirect to={'/login'}/>)
}
**I'm mainly looking for a syntax solution as opposed to the logic solution as I am aware of the logic behind this
I have a state which is an empty array:
constructor(props) {
super(props);
this.state = {
test_steps: [],
};
}
I need to fill up that state with the following data that get when I do a GET request:
See image
UPDATED:
export function copyTestScenarioLog(tSL) {
console.log("\nCalled copyTestScenarioLog");
let url = config.base_url + `/test_scenario_logs`;
return fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + getUserToken() },
body: JSON.stringify({
_id: tSL._id['$oid']
})
})
.then(res => res.json())
.then(data => {
getTestStepLogs(data)
return data;
})
.catch(err => console.log(err));
}
export function getTestStepLogs(data) {
const id = data.test_step_logs[0].$oid;
let url = config.base_url + `/test_step_logs/${id}`;
return fetch(url, {
method: 'GET',
headers: { 'Authorization': 'Bearer ' + getUserToken() }
})
.then(res => res.json())
.then(data => {
console.log(data)
return data
})
.catch(err => console.log(err));
}
How do I update my state after doing a GET fetch?
This is full react component code, you see how I call your funciton in componentDidMount, and in here I pass 'this' as an argument to copyTestScenarioLog.
import React, { Component } from 'react';
import copyTestScenarioLog from './copyTestScenarioLog';
class Component1 extends Component {
constructor(props) {
super(props);
this.state = {
test_steps: []
};
}
componentDidMount() {
var reactComponent = this;
copyTestScenarioLog('i dont konw that is tsl', reactComponent);
}
render() {
return (
<div></div>
);
}
}
export default Component1;
In 'copyTestScenarioLog', I get that ( refers to react component), and use setState function in react.
export function copyTestScenarioLog(tSL, reactComponent) {
console.log("\nCalled copyTestScenarioLog");
let url = config.base_url + `/test_scenario_logs`;
return fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + getUserToken() },
body: JSON.stringify({
_id: tSL._id['$oid']
})
})
.then(res => res.json())
.then(data => {
getTestStepLogs(data)
reactComponent.setState({
test_steps: data
});
return data;
})
.catch(err => console.log(err));
}
But basically I don't use this approach, I just wanted to show that how its done, I usually use await/async or function generators, because they are better approaches. Search about them and learn to use them.
you can pass onSuccess function into your getTestStepLogs and update your state.
export function getTestStepLogs(data , onSuccess) {
const id = data.test_step_logs[0].$oid;
let url = config.base_url + `/test_step_logs/${id}`;
return fetch(url, {
method: 'GET',
headers: { 'Authorization': 'Bearer ' + getUserToken() }
}).then(resp => {
if (onSuccess)
onSuccess(resp);
}).catch(err => console.log(err));
}
and when you call getStepLogs pass onSuccess as props:
this.props.getTestStepLogs(data , (resp)=>{
this.setState({test_steps:resp});
})
if you are using the get call at multiple place, you can be little generic and try this approach.
//return here does not do anything right now
export function getTestStepLogs(data) {
return new Promise(function(resolve, reject){
const id = data.test_step_logs[0].$oid;
let url = config.base_url + `/test_step_logs/${id}`;
return fetch(url, {
method: 'GET',
headers: { 'Authorization': 'Bearer ' + getUserToken() }
})
.then(res => res.json())
.then(data => {
console.log(data)
resolve(data);
})
.catch(err => {console.log(err);
reject(err);
});
})
}
async componentDidMount() {
let data = await copyTestScenarioLog();
//now set it to state
}
For an async call, there are three states. Call initiation, call success and call failure. Say "isLoading" represents the status of the call being running in the background.
Initially =>
{
isLoading : false,
data : '',
err : ''
}
Call initiated =>
{
isLoading : true,
data : '',
err: ''
}
Call success =>
{
isLoading : false,
data : response.data
err: ''
}
Call failed =>
{
isLoading :false,
data : '',
err: err
}
Usually, the GET calls of a component are made in componentDidMount. It is also the suggested way as per the react documentation.
componentDidMount(){
//Call Initiation
this.setState({
isLoading : true,
data : '',
err: ''
});
makeApiCall(api).
then((response) => {
this.setState({
isLoading : false,
data : response.data
err: ''
});
},
(err) => {
this.setState({
isLoading :false,
data : '',
err: err
})
})
.catch((err) => {
this.setState({
isLoading :false,
data : '',
err: err
})
})
}
I am using preact(light version of react) but syntax is almost the same. I am having an issue displaying verified after setting state from promise result. This is my container component:
import { h, Component } from "preact";
import { VerifierService } from "services/verifierService";
var CONFIG = require("Config");
//import * as styles from './profile.css';
interface PassportProps { token?: string; path?: string }
interface PassportState { appId?: string; verified?: boolean }
export default class Passport extends Component<PassportProps, PassportState> {
constructor(props) {
super(props);
this.state = { appId: CONFIG.Settings.AppId };
}
async componentDidMount() {
console.log("cdm: " + this.props.token);
if (this.props.token != undefined) {
await VerifierService.post({ token: this.props.token })
.then(data => {
this.setState({ verified: data.result });
console.log(JSON.stringify(data, null, 4));
})
.catch(error => console.log(error));
}
}
render() {
return <div>Test: {this.state.verified}</div>;
}
}
I can see console.log as true inside of promise result, but i can't display it in view.
Your data in your console.log is true, so therefor data.result will give you undefined. Try to just set the data in setState.
await VerifierService.post({ token: this.props.token })
.then(data => {
this.setState({ verified: data });
console.log(JSON.stringify(data, null, 4));
})
.catch(error => console.log(error));