How to get response from get request (React)? - reactjs

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

Related

React and promise issues with fetch method

i'm new in react and i've got some issues with asynchronous fetch data :
i want to fetch github users
function fetchUser(username) {
return fetch(`https://api.github.com/users/${username}`)
.then(response => response.json())
.then(data => data)
}
export function getUserData(username) {
const object = {
profile: fetchUser(username),
}
console.log(object)
return object
}
and this is my method in my component
componentDidMount() {
getUserData(this.props.playerOne)
}
but this is what i got in my console
{profile: Promise}
i'm sure that i dont understand well this promise so could you help me to have not a Promise in my object but the data i'm fetching ? (if i log data in my fetch i got what i want)
You can make this function async and wait for the promise resolution.
export async function getUserData(username) {
const object = {
profile: await fetchUser(username),
}
console.log(object)
return object
}
then in your componentDidMount
componentDidMount() {
getUserData(this.props.playerOne)
.then((profile) => { this.setState({ profile }) })
}

How to test an async function which does an axios request to an api using JEST in a React application

I am trying to test a async/await function which does an api call using axios to get users. I am new to testing React applications and using JEST (trying first time), unable to get the test running.
I have tried using mock function in JEST. My Code is as follows:
// component --users
export default class Users extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.getUsers = this.getUsers.bind(this);
}
/*does an api request to get the users*/
/*async await used to handle the asynchronous behaviour*/
async getUsers() {
// Promise is resolved and value is inside of the resp const.
try {
const resp = await axios.get(
"https://jsonplaceholder.typicode.com/users"
);
if (resp.status === 200) {
const users = resp.data;
/* mapped user data to just get id and username*/
const userdata = users.map(user => {
var userObj = {};
userObj["id"] = user.id;
userObj["username"] = user.username;
return userObj;
});
return userdata;
}
} catch (err) {
console.error(err);
}
}
componentDidMount() {
this.getUsers().then(users => this.setState({ users: users }));
}
/*****************************************************************/
//props(userid ,username) are passed so that api call is made for
//getting posts of s psrticular user .
/*****************************************************************/
render() {
if (!this.state.users) {
return (
<div className="usersWrapper">
<img className="loading" src="/loading.gif" alt="Loading.." />
</div>
);
}
return (
<div className="usersWrapper">
{this.state.users.map(user => (
<div key={user.id}>
<Posts username={user.username} userid={user.id} />
</div>
))}
</div>
);
}
}
//axios.js-mockaxios
export default {
get: jest.fn(() => Promise.resolve({ data: {} }))
};
//users.test.js
describe("Users", () => {
describe("componentDidMount", () => {
it("sets the state componentDidMount", async () => {
mockAxios.get.mockImplementationOnce(
() =>
Promise.resolve({
users: [
{
id: 1,
username: "Bret"
}
]
}) //promise
);
const renderedComponent = await shallow(<Users />);
await renderedComponent.update();
expect(renderedComponent.find("users").length).toEqual(1);
});
});
});
the test fails -
FAIL src/components/users.test.js (7.437s) ● Users ›
componentDidMount › sets the state componentDidMount
expect(received).toEqual(expected)
Expected value to equal:
1
Received:
0
Please help me figure out the problem. i am totally new to testing reactapps
It looks like similar to this one.
The problem is that test if finished earlier then async fetchUsers and then setState (it's also async operation). To fix it you can pass done callback to test, and put the last expectation into setTimeout(fn, 0) - so expect will be called after all async operations done:
it("sets the state componentDidMount", (done) => {
...
setTimeout(() => {
expect(renderedComponent.find("users").length).toEqual(1);
done();
}, 0);
});
As mentioned in comment, it's hacky fix, I hope here will be other answers with more jest way to fix it.
As far as I understood, what you are trying to do, is to wait for the resolution of a promise, which is 'hidden' inside of your componentDidMount() method.
expect(renderedComponent.find("users").length).toEqual(1);
will not work in my view in this context because find() is trying to select DOM Elements. If you want to check the state, you need to use state():
expect(renderedComponent.state("users").length).toEqual(1);
Still, you will have to find the right way to wait for the resolution of the promise:
To refer to the previous posts and comments, I don't see any effect in using async/await in combination with any of the enzyme methods (update, instance or whatsoever. Enzyme docs also give no hint in that direction, since the promise resolution is not the job of enzyme).
The only robust, clean and (jest-default) way forward is somehow a mixture of the different approaches. Here comes your code slightly changed:
// component --users
export default class Users extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.getUsers = this.getUsers.bind(this);
}
/*does an api request to get the users*/
/*async await used to handle the asynchronous behaviour*/
async getUsers() {
// Promise is resolved and value is inside of the resp const.
try {
const resp = await axios.get(
"https://jsonplaceholder.typicode.com/users"
);
if (resp.status === 200) {
const users = resp.data;
/* mapped user data to just get id and username*/
const userdata = users.map(user => {
var userObj = {};
userObj["id"] = user.id;
userObj["username"] = user.username;
return userObj;
});
return userdata;
}
} catch (err) {
console.error(err);
}
}
componentDidMount() {
// store the promise in a new field, not the state since setState will trigger a rerender
this.loadingPromise = this.getUsers().then(users => this.setState({users: users}));
}
/*****************************************************************/
//props(userid ,username) are passed so that api call is made for
//getting posts of s psrticular user .
/*****************************************************************/
render() {
if (!this.state.users) {
return (
<div className="usersWrapper">
<img className="loading" src="/loading.gif" alt="Loading.."/>
</div>
);
}
return (
<div className="usersWrapper">
{this.state.users.map(user => (
<div key={user.id}>
<Posts username={user.username} userid={user.id}/>
</div>
))}
</div>
);
}
}
//axios.js-mockaxios
export default {
get: jest.fn(() => Promise.resolve({data: {}}))
};
//users.test.js
describe("Users", () => {
describe("componentDidMount", () => {
it("sets the state componentDidMount",() => {
mockAxios.get.mockImplementationOnce(
() =>
Promise.resolve({
users: [
{
id: 1,
username: "Bret"
}
]
}) //promise
);
const renderedComponent = shallow(<Users/>);
//Jest will wait for the returned promise to resolve.
return renderedComponent.instance().loadingPromise.then(() => {
expect(renderedComponent.state("users").length).toEqual(1);
});
});
});
});
While doing some research, I came across this post: To be honest I don't know whether it is a good idea, but it works in my async tests, and it avoids the additional reference to the promise in your component. So feel free to try this out also!

How to write a callback function for asynchronous calls from helper method to componentDidMount using React JS

I have a helper js file and in that have a asynchronous calls method, in my component am importing the helper js file and in componentDidMount, am making the call.
Before ajax completion, the value is getting returned as undefined. Is there a way to write a callback function, on success it should come to my componentDidMount method.
This is what I tried:
helper.js
export const asynchronous = data => {
axios.get(url).then(res => {
return res;
});
}
component.js
import {asynchronous} from 'helper';
componentDidMount(){
const newResp = asynchronous(this.props.data);
console.log(newResp); // returned before api success/failure, should be returned after api request
}
Use async-await
async componentDidMount(){
const newResp = await asynchronous(this.props.data);
console.log(newResp); // returned before api success/failure, should be returned after api request
}
And your asynchronous code. You're missing a return statement.
export const asynchronous = data => {
return axios.get(url).then(res => {
return res;
});
}
An Example : Async Await with React Lifecycle methods
You can return the intial axios promise and then use its then,
export const asynchronous = data => {
return axios.get(url);
}
And use it in your componentDidMount
componentDidMount(){
const newResp = asynchronous(this.props.data).then(res => {
console.log(res);
});
}
Need to add async to componentDidMount then await the response.
// helper.js
async function getData(url) {
const response = await axios.get(url);
console.log(response);
return response;
}
export default getData;
In your component:
import { getData } from './helper.js'; // <-- Not 'helper' which looks in node modules
...
componentDidMount = async () => {
const newResp = await asynchronous(this.props.data);
console.log(newResp);
}
...
More than that, you will want to do something with that response.
...
componentDidMount = async () => {
const newResp = await asynchronous(this.props.data);
this.setState({ data: newResp })
}
...

AsyncStorage not working properly

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

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

Resources