Getting object promise instead of value - reactjs

login(loginId, password) {
return axios
.post(API_URL + "login", {
loginId,
password
})
.then(response => {
console.log(response.data);
if (response.data) {
localStorage.setItem("token", JSON.stringify(response.data));
localStorage.setItem("user", this.getUser(loginId));
console.log(localstorage.getItem("user");
}
console.log(response.data);
return response.data;
});
}
getUser(loginId){
return axios
.get(API_URL+"user/search/"+loginId,{
headers: { Authorization: `Bearer ${authHeader()} ` },
});
getCurrentUser() {
return (JSON.parse(localStorage.getItem('user')));
}
}
class ViewMytweetComponent extends Component {
constructor(props) {
super(props)
this.onChangeReply = this.onChangeReply.bind(this);
this.state = {
Tweet: [],
reply: "",
user: AuthService.getCurrentUser()
}
this.deleteTweet = this.deleteTweet.bind(this);
}
componentDidMount() {
const { user } = this.state;
console.log(user);
var userId = user.loginId;
TweetDataService.getMyTweet(userId).then((res) => {
this.setState({ Tweet: res.data });
// console.log(this.state.Tweet);
});
}
}
In the login method I call the getUser method and store its return value to localStorage with the key user. The getCurrentUser method is used to return the stored user-item from the localStorage object.
Requesting the previously stored user in the componentDidMount method however fails. Logging the user object to the console produces:
[object Promise].
Does anyone know how to solve this?

since axios.get returns a promise, the getUser method is also returning a promise too. Which is an object, when you try to save it in localStorage in here:
localStorage.setItem("user", this.getUser(loginId));
JavaScript automaticaly converts it to a string, which becomes: [object Promise].
There are a few ways to solve this, for example:
login(loginId, password) {
return axios
.post(API_URL + "login", {
loginId,
password
})
.then(response => {
console.log(response.data);
if (response.data) {
// store the result instead of the promise itself,
// also stringify the result before javascript creates a meaningless string itself.
this.getUser(loginId).then((user)=>localStorage.setItem("user", JSON.stringify(user))
localStorage.setItem("token", JSON.stringify(response.data));
console.log(response.data);
return response.data;
})
}
Of course nested thens aren't exactly a good practice, so maybe it would be nice to rethink class' overal data fetching logic.

Related

What is the correct way to pass a token to axios from React?

The question sounds vague so allow me to explain. I am wondering, what is the correct/best way to pass get a token from local storage and pass it into my axios request.
This is what I am doing now, and I am sure this is not correct so I want to fix it but am unsure how.
I have a component called TicketsComponent that requires authorization. Therefore, in componentDidMount(), I validate the token, and if its invalid then send the user back to login, otherwise, load the tickets. It looks like this:
componentDidMount() {
this._isMounted = true;
// validate token
const token = localStorage.getItem("token");
AuthService.validateToken()
.then((res) => {
if (res == undefined || res == null || !token) {
this.props.history.push("/login");
}
})
.then(() => {
TicketService.getTickets().then((res) => {
if (this._isMounted) {
this.setState({ tickets: res.data });
}
});
});
}
Both AuthService.validateToken() and TicketService.getTickets() require the JWT in the header. Here are those two functions:
validateToken() {
return axios
.get(API_BASE_URL + "authenticate", {
headers: {
token: this.getTokenFromLocalStorage(),
},
})
.then("did it")
.catch((error) => {
console.log("failed to validate token" + error);
return null;
});
}
getTickets() {
console.log("getting tickets!");
console.log("Environment variable: " + process.env.NODE_ENV);
return axios
.get(API_BASE_URL + "tickets", {
headers: { Authorization: `Bearer ${this.getTokenFromLocalStorage()}` },
})
.then("yessssss")
.catch((error) => {
console.log("failed to get tickets" + error);
});
}
The problem is that both AuthService and TicketService share the same function called getTokenFromLocalStorage(). That looks like this:
getTokenFromLocalStorage() {
const token = localStorage.getItem("token");
console.log("the token is -> " + token);
if (token === null) {
return undefined;
}
return token;
}
catch(err) {
return undefined;
}
So obviously this is not ideal. I have the same function in two services just to get the token from the header. What is the recommended way of doing this?
EDIT: I hope this kind of question is allowed. Even though the code is not actually broken per se, I still think this is useful to beginners like me to implement best practice.
You can create a shared axios instance like so:
const API_BASE_URL = 'https://example.com/api/'
const instance = axios.create({
baseURL: API_BASE_URL,
headers: { Authorization: `Bearer ${this.getTokenFromLocalStorage()}` },
});
Then you'd just import "instance" into the components and call:
import {instance} from '../wherever' // decide if you want to import default or not
// make sure to either include or exclude the / in the first parameter passed into the request method (e.g. '/authenticate' or 'authenticate') below based on whether you provided a / in the API_BASE_URL
instance.post('authenticate', {
// any additional config relevant to the request, e.g:
data: {
username: 'my user!',
password: 'super_secret_password'
}
})

React object fetched from API is missing properties

I am fetching a profile object from my API following user authentication. The fetch returns the profile object as expected, however my server logger clearly shows a profile object containing an "id" and "username", but the initial object returned to the client has only the "username". I am only able to access the "id" property of the profile abject after I refresh.
Not sure how to fix this, but ive tried everything I can think of...
Login Form
export default class LoginForm extends Component {
static defaultProps = {
onLoginSuccess: () => { }
}
state = { error: null }
handleSubmitJwtAuth = ev => {
ev.preventDefault()
this.setState({ error: null })
const { username, password } = ev.target
//login request
AuthApiService.postLogin({
username: username.value,
password: password.value,
})
//login response
.then(res => {
//updates context profile with username value after login
this.props.updater({ username: username.value })
username.value = ''
password.value = ''
TokenService.saveAuthToken(res.authToken)
this.props.onLoginSuccess()
})
.catch(res => {
this.setState({ error: res.error })
})
}
Profile API Service
const ProfileApiService = {
getProfile() {
return fetch(`${config.API_ENDPOINT}/profile`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `bearer ${TokenService.getAuthToken()}`
}
})
.then(res => {
return (!res.ok)
? res.json().then(e => Promise.reject(e))
: res.json()
}
);
}
}
(API) Profile Service
const ProfileService = {
getProfile : (db,id) =>{
return db
.from('v_users')
.select('id','username')
.where({id})
.first();
},
serializeProfile(profile){
return {
id: profile.id,
username: xss(profile.username)
};
}
}
initially, console.log(this.state.profile.id) //undefined
after a refresh, console.log(this.state.profile.id) // 7
the server log shows this object being returned initially
{ id: 7, username: 'qber83' }, however as mentioned above, I am unable to access the "id" property without refreshing the browser.
The problem here could be that your state is not updated properly, since the object returned is right the API services work, so here your context updater or this.props.onLoginSuccess() might contain the issue.

Cannot fetch api due to array react native

I bulid an api using laravel which can run in postman (http://lkcfesnotification.000webhostapp.com/api/notifications). The problem is when i fetch using an example from this (https://www.youtube.com/watch?v=IuYo009yc8w&t=430s) where there is a array in the api then i have to setstate the array which is working well but when i try using the below code it does not render due to it is not using array in the api for example the random user api have "results" :[item], and mine one is "data":[my item]
fetchData = async () => {
const response = await fetch("https://randomuser.me/api?results=500");
const json = await response.json();
this.setState({ data: json.results });
};
if i use this will work but i want to use below code due to some homework i am doing
type Props = {};
export default class IndexScreen extends Component<Props> {
...
this.state = {
data: [],
isFetching: false,
};
_load() {
let url = "http://lkcfesnotification.000webhostapp.com/api/notifications";
this.setState({isFetching: true});
fetch(url)
.then((response) => {
if(!response.ok) {
Alert.alert('Error', response.status.toString());
throw Error('Error ' + response.status);
}
return response.json()
})
.then((members) => {
this.setState({data});
this.setState({isFetching: false});
})
.catch((error) => {
console.log(error)
});
}
https://imgur.com/a/he5mNXv this is my render
the result i get the code i run is blank is loading
The fetch request is working but you are not saving the right data in the right state property.
The issues is located in the following part:
.then((members) => {
this.setState({data});
this.setState({isFetching: false});
})
You are assigning the response to a variable members but saving another variable data, which does not exist.
In addition, the response is an object with more information than just the data, so what you are looking for is just the data property of the response.
This should work:
.then(({ data }) => {
this.setState({data});
this.setState({isFetching: false});
})
Here we destructure the response into the variable { data }, solving your issue.
Based on the snippets you don't use the fetched data to set it to your state:
.then((members) => {
this.setState({data});
this.setState({isFetching: false});
})
membersis the result of your fetched json. So either rename members to data or use data: members. If the code should work like your first function it's probably data: members.result. You can also combine the two setState calls to one single call:
this.setState({
data: members.result,
isFetching: false,
});

AsyncStorage.getItem in react native not working as expected

I am trying to fetch data using AsyncStorage. whenever i call my action creator requestData and do console on the data which is passed , i get something like below .I have two version of getItem .In both the version i get useless value for property field . Property value should be readable
{"fromDate":"20160601","toDate":"20160701","property":{"_40":0,"_65":0,"_55":null,"_72":null},"url":"/abc/abc/xyz"}
async getItem(item) {
let response = await AsyncStorage.getItem(item);
let responseJson = await JSON.stringify(response);
return responseJson;
}
async getItem(item) {
try {
const value = AsyncStorage.getItem(item).then((value) => { console.log("inside componentWillMount method call and value is "+value);
this.setState({'assetIdList': value});
}).then(res => {
return res;
});
console.log("----------------------------value--------------------------------------"+value);
return value;
} catch (error) {
// Handle errors here
console.log("error is "+error);
}
}
componentWillMount() {
requestData({
fromDate: '20160601',
toDate: '20160701',
assetId: this.getItem(cmn.settings.property),
url: '/abc/abc/xyz'
});
}
You are getting property as a promise, you need to resolve it.
Try to use something link that.
assetId: this.getItem(cmn.settings.property).then((res) => res)
.catch((error) => null);
Since AsyncStorage is asynchronous in nature you'll have to wait for it to return the object AND THEN call your requestData method; something like the following -
class MyComponent extends React.Component {
componentWillMount() {
this.retrieveFromStorageAndRequestData();
}
async getItem(item) {
let response = await AsyncStorage.getItem(item);
// don't need await here since JSON.stringify is synchronous
let responseJson = JSON.stringify(response);
return responseJson;
}
async retrieveFromStorageAndRequestData = () => {
let assetId = await getItem(cmn.settings.property);
requestData({
fromDate: '20160601',
toDate: '20160701',
assetId,
url: '/abc/abc/xyz'
}) ;
}
// rest of the component
render() {
// render logic
}
}

Very slow response from action creator React/Redux

I am getting super slow response times (upwards 10 seconds) for a function to be called in my action creator.
export function acceptContract(id) {
return function(dispatch) {
const config = { headers: { authorization: localStorage.getItem('etherToken') } };
const data = { data: id };
axios.put('/pending-contracts/accept',
data,
config
).then( response => {
console.log(response);
getPendingContracts();
})
.catch( response => {
// If the get doesn't work, boot the user out to index.
console.log(response);
});
}
}
I update one of the values of contracts in my DB, and I want redux to then dispatch the new list for the user to show the update on the UI.
Not sure why the getPendingContract() invocation takes so long. I get the response from my backend almost immediately.
export function getPendingContracts() {
return function(dispatch) {
axios.get('/pending-contracts', {
headers: { authorization: localStorage.getItem('etherToken') }
})
.then( response => {
console.log('in getPendingContracts')
return dispatch({
type: PENDING_CONTRACTS_LIST,
payload: response.data.message
});
})
.catch( response => {
// If the get doesn't work, boot the user out to index.
console.log(response);
});
}
}
The issue might be related to how you are calling getPendingContracts from acceptContract. You are just calling the function directly, without dispatching it. As far as i can tell all that would do is return you a function that never gets invoked, not sure how you get a response at all. Change the call to this:
then( response => {
console.log(response);
dispatch(getPendingContracts());
})

Resources