I add the setItem function of AsyncStorage in the constructor of my Component and then I made a function to fetch it's value using the getItem function, but it returns empty. Am I missing something?
Here's my code:
constructor(props) {
super(props);
try {
AsyncStorage.setItem('testKey', 'I like to save it.');
} catch (error) {
alert("error", error);
// Error saving data
}
}
getData(){
AsyncStorage.getItem('testKey').then((data) => {
alert("data", data);
})
}
Basically, I want to store an accessToken to AsyncStorage, and once it's set, I want to call the getItem function to use it.
Also, the then() function for the setItem is not working:
AsyncStorage.setItem('testKey','blah blah').then(() => {
console.log("I never get executed."); // -> this code never executes for some reason
});
I've been trying to resolve it, but to no avail.
Keep in mind that both (setItem and getItem) are executed asynchronous, a async/await should solve your problem, as explained in the documentation:
constructor(props) {
super(props);
this.setData();
}
async setData() {
try {
await AsyncStorage.setItem('testKey', 'I like to save it.');
} catch (error) {
// Error setting data
}
}
async getData(){
try {
const value = await AsyncStorage.getItem('testKey');
if (value) {
// We have data!!
console.log(value);
}
} catch (error) {
// Error retrieving data
}
}
Related
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}>.
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 ;)
I am learning about how to use synchronous setState but it is not working for my project. I want to update the state after I get the listingInfo from Axios but it does not work, the res.data, however, is working fine
class ListingItem extends Component {
constructor(props) {
super(props);
this.state = {
listingInfo: {},
open: false,
};
this.getListingData(this.props.itemId);
}
setStateSynchronous(stateUpdate) {
return new Promise((resolve) => {
this.setState(stateUpdate, () => resolve());
});
}
getListingData = async (item_id) => {
try {
const res = await axios.get(`http://localhost:5000/api/items/${item_id}`);
console.log(res.data);//it's working
await this.setStateSynchronous({ listingInfo: res.data });
// this.setState({
// listingInfo: res.data,
// });
console.log(this.state.listingInfo);//no result
} catch (err) {
setAlert('Fail to obtain listings', 'error');
}
};
I would be really grateful for your help!
Thanks to #PrathapReddy! I used conditional rendering to prevent the data from rendering before the setState is done. I added this line of code on the rendering part:
render() {
if (Object.keys(this.state.listingInfo).length === 0) {
return (
<div>
Loading
</div>
);
} else {
return //put what you want to initially render here
}
}
Also, there is no need to modify the setState, the normal setState will do. Hope this is useful!
I have a react component which calling fetch/abort on mount/unmount,
when I tried to move the fetch function to a separated class the "then" function is keep calling even after aborting the fetch request so the "Not Aborted" text is printed to the console.
It looks like this:
ReactComponent
class ReactComponent extends Component {
constructor(props) {
super(props);
this.TodoService = new TodoService();
}
componentDidMount() {
this.TodoService.fetch().then(res => {
console.log("Not Aborted");
});
}
componentWillUnmount() {
this.TodoService.abort();
}
}
TodoService
class TodoService {
constructor() {
this.controller = new AbortController();
this.signal = this.controller.signal;
}
handleErrors(response) {
if (response.ok) {
return response;
}
throw Error(response.statusText);
}
fetch() {
return fetch(`todos`, {
signal: this.signal
})
.then(this.handleErrors)
.then(res => res.json())
.catch(err => console.error(err));
}
abort() {
this.controller.abort();
}
}
I have a feeling it could come down to the this context.
try adding these to the TodoService constructor:
this.fetch = this.fetch.bind(this);
this.abort = this.abort.bind(this);
You are calling those methods from a different context and the current contexts this will be used.
Here is the browser support for the AbortController
You can also try this:
fetch() {
return fetch(`todos`,
{signal: this.signal},
this.controller.abort()
)
.then(this.handleErrors)
.then(res => res.json())
.catch(err => console.error(err));
}
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
}
}