AsyncStorage.getItem in react native not working as expected - reactjs

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
}
}

Related

How to get response from get request (React)?

I am new guy in react, so how to put value that i receive from jsonHandler function into render return statement?
I've tried a lot, but i always have the same result. Console.log(jsonData) in jsonHandler function returns value that i need, but function jsonHandler returns promise and idk how to handle it. It doesn't matter to use axios.get or fetch().
async function jsonHandler () {
let jsonData;
const url = "http://localhost/index.php";
await axios.get(url)
.then(({data}) => {
jsonData = data.data;
});
console.log(jsonData); //returns that i need
return jsonData;
}
class Menu extends Component {
...
render() {
console.log(jsonHandler()); //returns promise
return <div className="MenuWrap">{this.mainHandler(Here Must Be Data From jsonHandler)}</div>;
}
}
export default Menu;
You can do it this way. Make use of states for reactive updates. I referred to https://stackoverflow.com/a/45744345/13965360 for the setStateAsync function, which sets the state value asynchronously and means it will wait till the API call is done. You can use try and catch with await if you are using async instead of then and catch blocks.
const url = "http://localhost/index.php";
class Menu extends Component {
state = {
jsonData: {},
};
//creating a function that sets the state asynchronously
setStateAsync(state) {
return new Promise((resolve) => {
this.setState(state, resolve);
});
}
// Fetch the data
async jsonHandler() {
try {
const response = await axios.get(url);
this.setStateAsync({ jsonData: response.data });
console.log(this.state.jsonData); //returns that i need
} catch (error) {
throw new Error(error);
}
}
render() {
return (
<div className="MenuWrap">
{Object.keys(this.state.jsonData).length &&
JSON.stringify(this.state.jsonData)}
</div>
);
}
}
export default Menu;
If you want to do the API call instantly once the component renders, you need to put it in the componentDidMount lifecycle.
Like, async componentDidMount() { await this.jsonHandler(); }
Or, if you want to make the API call upon clicking a button, you need to bind the method to the listener like <button onClick={this.jsonHandler}>.

Return the fetch response from another file

I am trying to call a function that calls fetch to an API from a React component in a separate file and am not finding the correct solution to get the correct response back.
When I debug, the result returns before the updateAccount function has completed and the final result is never returned to my update function.
Inside the fetch, the API returns the correct response whether it is successful or has validation errors and those results are correctly assigned to result.success and result.errors but the result doesn't get returned from the function so that the caller can make use of those values.
Inside of my React component:
import { updateAccount } from '../services/requests';
...
const update = (account: EditAccountModel) => {
const result = updateAccount(account);
if(result.errors.length > 0) {
// will notify of errors
console.log(result.errors); // is an empty array instead of validation errors
} else {
// will notify of success
console.log(result.success); // is an empty string instead of success message
}
}
...
My request file
export const updateAccount = (account: EditAccountModel | undefined): EditAccountResponseModel => {
const result = new EditAccountResponseModel();
fetch(baseUrl, {
method: 'PUT',
body: JSON.stringify(account),
headers
})
.then(response => {
if (!response.ok) {
return Promise.reject(response);
}
result.success = `${account?.name} was updated successfully!`
})
.catch(error => {
if (typeof error.json === "function") {
error.json().then(jsonError => {
result.errors.push(jsonError);
}).catch(genericError => {
result.errors.push(genericError);
});
}
});
return result;
}
The result reassignment happens inside then catch but it won’t be affective in the way you expected. The guaranteed way to return correct result is via a callback() passed to your updateAccount() if you could afford it:
export const updateAccount = (
account: EditAccountModel | undefined,
callback: Function
): EditAccountResponseModel => {
const result = new EditAccountResponseModel();
fetch(baseUrl, {
method: 'PUT',
body: JSON.stringify(account),
headers
})
.then(response => {
if (!response.ok) {
return Promise.reject(response);
}
result.success = `${account?.name} was updated successfully!`
callback(result);
})
.catch(error => {
if (typeof error.json === "function") {
error.json().then(jsonError => {
result.errors.push(jsonError);
callback(result);
}).catch(genericError => {
result.errors.push(genericError);
callback(result);
});
}
});
}
And inside your React component:
const update = (account: EditAccountModel) => {
const handleResult = (res) => {
// your result callback code
// ...
};
updateAccount(account, handleResult);
// ...
}
Alternative way that keeps your current structure is to change your current updateAccount() to an async function, then return await fetch().
You need to wait for the response . I'll let read more about how Promise work in JavaScript.
I wouldn't code updateAccount the same way you did, especially where you use the variable result and update it inside the flow of the promise (you really don't need that). You're also using React so you can use the state to store and update the result of the update function. But let's fix your problem first:
export const updateAccount = async (account: EditAccountModel | undefined): EditAccountResponseModel => {
const result = new EditAccountResponseModel();
await fetch(baseUrl, {
method: 'PUT',
body: JSON.stringify(account),
headers
})
.then(response => {
if (!response.ok) {
return Promise.reject(response);
}
result.success = `${account?.name} was updated successfully!`
})
.catch(error => {
if (typeof error.json === "function") {
error.json().then(jsonError => {
result.errors.push(jsonError);
}).catch(genericError => {
result.errors.push(genericError);
});
}
});
return result;
}
First make your function updateAccount async then await the result of the promise.
Now the same thing for the function update:
const update = async (account: EditAccountModel) => {
const result = await updateAccount(account);
if(result.errors.length > 0) {
// will notify of errors
} else {
// will notify of success
}
}

Issue with displaying data returned from REST API using React

I am trying out some stuff using the react-chatbot-kit in the front end and getting data from a REST API. Console.log shows the data inside .then, however I am getting the error "Uncaught TypeError: Cannot read property 'map' of undefined" when trying to output the data on the console inside the calling function. I need help to display the returned data in console.log in the function handleApiList(). Thanks in advance.
PS: I am a newbie of course in React :) since I am not clear on how to handle REST API calls that are done asynchronously. Look forward to getting this resolved. Any help and tips on resolving this will be greatly appreciated
Following is the code:
// ActionProvider starter code
class ActionProvider {
constructor(createChatBotMessage, setStateFunc) {
this.createChatBotMessage = createChatBotMessage;
this.setState = setStateFunc;
this.state = {
error: null,
users: []
}
}
greet() {
const greetingMessage = this.createChatBotMessage("Hi! Greeting!")
this.updateChatbotState(greetingMessage)
}
// This is being called when the user types in 'api' in chat window
handleApiList()
{
const { error, users } = this.state;
this.getData();
if(error) {
console.log("Error: ", error.message)
}
else {
let myarray=[]
users.map(function(user)
{
myarray += `${ user.name }\n`;
return `${ user.name }`;
})
console.log(myarray)
}
}
getData()
{
console.log("in now")
fetch("https://jsonplaceholder.typicode.com/users")
.then(res => res.json())
.then(
(result) => {
this.setState({
users: result
});
},
(error) => {
this.setState({ error });
}
)
}
handleJobList = () => {
const message = this.createChatBotMessage(
"Fantastic, I've got the following jobs available for you",
{
widget: "jobLinks",
}
);
this.updateChatbotState(message);
};
updateChatbotState(message) {
// NOTE: This function is set in the constructor, and is passed in
// from the top level Chatbot component. The setState function here
// actually manipulates the top level state of the Chatbot, so it's
// important that we make sure that we preserve the previous state.
this.setState(prevState => ({
...prevState, messages: [...prevState.messages, message]
}))
}
}
export default ActionProvider;
You are fetching in getData and it's an async function. The data is not ready. It's better to just return the data than to setting state.
simplified version of your code.
handleApiList()
{
const { error, users } = this.state;
const data = await this.getData();
//data is ready, do what u want with the data here.
}
}
const getData = async() => {
return fetch("https://jsonplaceholder.typicode.com/users")
.then(res => res.json())
)
}
.map returns an array, if you want to push u need to use forEach.
Example
let myarray=[]
data.forEach((user) =>
{
myarray.push(user.name });
})
console.log(myarray)
Issue description:
const { error, users } = this.state; // gets state values
this.getData(); // updates state values
if(error) {
console.log("Error: ", error.message)
}
else {
let myarray=[]
users.map(function(user) // users is value before state update
I would suggest returning from getData() a promise with result of api call. After that you can execute code in handleApiList() in .then().
Proposal:
getData()
{
console.log("in now")
return fetch("https://jsonplaceholder.typicode.com/users")
.then(res => res.json())
.then(
(result) => {
this.setState({
users: result
});
return result;
}
)
}
I would also move error handling to .catch().
Also have a look on this. Working using async/await instead of pure Promises is easier and cleaner ;)

how to update a state variable immediately

How should I code so that I can save the API response to the state immediately and access the updated response outside the setState().
state={
response: []
}
this.setState({
response: res // res is coming from API response
});
console.log(this.state.response); // it should show the API response instead of empty []
Use callbacks:
...
this.setState({
response: res // res is coming from API response
}, function() {
console.log(this.state.response);
});
...
Make the method calling setState to be async, and let res await answer. Something like
yourMethod = async (params) => {
const res = await //api call
this.setState({
response: res
)}
}
componentDidMount is the recommended lifecycle hook to fetch initial state data
class App extends React.Component {
state = {
data: [],
}
async componentDidMount() {
const res = await fetch(url);
const data = await res.json();
this.setState({ data }, console.log('state updated', this.state));
}
render() {
return (
/* render data */
);
}
}

React native not waiting for response from API before continuing

I have just started playing about with react native and I have a problem that functions aren't waiting for responses before continuing.
So in Chrome my console log displays:
userStore
this state contents
returned data from api / userstore [object Object]
Basically getUserDetails is executed and in that time while the api is being called the setData function runs, and it completes before the api result has been returned.
I would like the getUserDetails functio to complete before setData is called.
I have had a look at resources online, but am at a loss. The code I am using is below (This has been stripped down for ease of reading nb. I am using mobx)
UserScreen.js
constructor (props) {
super(props);
this.state = {
data: null
};
}
async componentDidMount() {
this.props.commonStore.setLoading(true);
await this.props.userStore.getUserDetails('1');
this.setData();
this.props.commonStore.setLoading(false);
}
setData() {
this.setState({
userDetails: this.props.userStore.userDetails
});
console.log('userStore' + this.props.userStore.userDetails)
console.log('this state contents '+ this.state.userDetails);
}
render () {
if(this.props.commonStore.isLoading===false) {
return (<View><Text>Ready!!</Text></View>)
}else{}
return (<View><Text>Loading</Text></View>)
}
}
UserStore.js
#action getUserDetails = (userID) => {
axios.get('http://192.168.1.9/user/' + userID)
.then(response => {
console.log('returned data from api / userstore ' +response.data.user);
this.userdetails = response.data.user;
}).catch(error => {
console.log(error);
this.error = error
}) }
Thanks
If you have stumbled upon the beauty of Mobx, you need to move towards a stateless solution i.e.:
UserScreen.js
componentDidMount() {
this.getUserDetails();
}
async getUserDetails(){
await this.props.UserStore.getUserDetails('1');
}
render () {
const { isLoading, userDetails, error } = this.props.UserStore
return (<View>
{(!!isLoading)?<Text>{userDetails}</Text>:<Text>Loading</Text>}
</View>)
}
UserStore.js
#observable userdetails = {};
#observable isLoading = false;
#observable error = {};
async getUserDetails(userID) {
this.isLoading = true;
try {
await axios.get('http://192.168.1.9/user/' + userID)
.then(response => {
console.log('returned data from api / userstore '+response.data.user);
this.userdetails = response.data.user;
this.isLoading = false;
})
.catch(error => {
console.log(error);
this.error = error
})
} catch (e) {
console.log('ERROR', e);
this.isLoading = false;
}
}
As you are passing the data into an observable array i.e. #observable userdetails = {}; Mobx will automatically update the state, once the promise / await is complete.
P.S. Mobx rules OK!! :o)

Resources