How to get response of a function in another file - javascript

I have two files named actions.js and vip.js. I have declared a function fetchVip in action file and imported it in vip file which will populate text element when screen is loaded. I want to access fetchVip response in vip file but i get undefined like this LOG useeffect undefined. While respone works as expected in action file.My code is below.
Vip File
import {fetchVip} from '../../Store/action';
const Vip = props => {
useEffect(() => {
console.log('useeffect', fetchVip());
}, [fetchVip()]);
action file
export const fetchVip = () => {
axios
.post(`${baseUrl}/get-vips`)
.then(function (response) {
return response.data;
})
.catch(function (error) {
console.log('error', error);
return {type: 'ERROR', payload: error};
});
};

fetchVip does not return anything, so you have to add a return statement here first:
export const fetchVip = () => {
return axios
.post(`${baseUrl}/get-vips`)
or remove the curly braces, then it will return as well.
export const fetchVip = () => axios
.post(`${baseUrl}/get-vips`)
...
return {type: 'ERROR', payload: error};
})
Now it will return a promise. That means that the result will not be there right away, but at some point later in time. Therefore, if you want to use it in the useEffect, you have to await for the result to arrive.
you could to this with the ES6 syntax:
useEffect(() => {
const getVip = async () => {
const vip = await fetchUsers();
console.log(vip)
//now you can do something with it
};
getVip();
}, [fetchVip]);
or the promise-then syntax:
useEffect(() => {
fetchVip().then(result => {
console.log(result);
//do something with the result
})
}, [fetchVip]);
This is wrong btw. remove the (). You want to check for the function here, not the result of the function.
}, [fetchVip()]);

Related

How do I access API data in my HTML in react

I have been trying to access the API data inside my html code but I could not instead this error message keep showing.
This is the error
message: App.js:13 Uncaught TypeError: data.map is not a function
Here is the code snippet of the data fetching
export async function getUsers () {
try {
const response = await fetch('https://cornie-assessment.herokuapp.com/users/8wdkcw05bdEa47R')
return response.json()
} catch (err) {
console.log(err);
}
}
Then I imported it to my app.js file. Here is also the code
import { useEffect, useState } from 'react';
import {getUsers} from './Components/Request'
function App() {
const[data, setData] = useState([])
useEffect(() => {
getUsers().then(data => {
setData(data)
console.log(data)
})
}, [])
return (
<div className="App">
{
data.map(items => (
<article>{items.data.email}</article>
))
}
</div>
);
}
export default App;
Please check your API response and the data which you're setting to state.
map function work on an array.
As I have seen your API response -> It's an object type.
{"status": true, "data": [...], ...}
So, you should set the state like the below.
getUsers().then(res => {
setData(res.data) // it will be an array
console.log(res) // it's an object
})
Your response is an object { status : true, data: [...] }. You are using an array method ( map ) on the object. Try this
getUsers().then(response => {
setData(response.data)
})
}, [])
problem is related assigning the response data as array
export async function getUsers () {
try {
const response = await fetch('https://cornie-assessment.herokuapp.com/users/8wdkcw05bdEa47R')
const result = await response.json()
return result.data; // now you have an array of objects
} catch (err) {
console.log(err);
}

How do I conditionally render data returned from a useEffect that calls an API route and adds that data to a useState variable

I have a React component that's main purpose is to display a list of profile names. The profile names are stored in a useState variable called profiles.
I have a useEffect in place on the component that effectively calls our API route to return the profile data we need on the frontend and place that data in the state variable for profiles.
If the profiles state variable has a length of zero, then we don't have the data and a logo will appear to load, otherwise the profiles should be mapped through and displayed as h1 tags.
While a console.log shows to me I am returning the data I need, I am getting the following error in my console "Uncaught TypeError: profiles.map is not a function".
Here is my code:
function ProfileListComponent() {
const fetchProfiles = async (payload) => {
const token = localStorage.getItem("token")
const response = await axios.get("http://127.0.0.1:5000/profiles", {headers:{
"Authorization": `Bearer ${token}`
}})
if (response.data) {
let profileData = []
for (let key in response.data) {
let profile = [
response.data[key].profile_id,
response.data[key].profile_name,
response.data[key].flow_rate,
response.data[key].hv,
response.data[key].lv,
response.data[key].duty_cycle,
response.data[key].frequency,
]
profileData.push(profile)
}
console.log(profileData)
return profileData
}
}
const [profiles, setProfiles] = useState([])
const compileProfileData = () => {
return ""
}
useEffect(() => {
try {
const profileData = fetchProfiles()
setProfiles(profileData)
} catch (error) {
console.log(error)
}
}, [])
return (
<div>
{profiles.length === 0 ?
<img src={logo} className="App-logo" alt="logo" />
: (
profiles.map(profile => <h1 className="profileBox" key={profile[0]} onClick={() => {compileProfileData(profile)}}>{profile[1]}</h1>
)
)}
</div>
)
}
I have tried different methods to conditionally render this data, though I always seem to error out with the same message indicating that the state variable isn't even an array (which is interesting considering its default value is an empty array).
Does anyone have some guidance to offer on how to correctly get this data rendered?
This happens because inside useEffect hook try-catch block executes both fetchProfiles and setProfiles synchronously. So setProfiles sets a promise which has not resulted yet and below map function means "Give me array, not a promise".You should put your setState inside fetchProfiles.
From this;
const fetchProfiles = async () => {
// const profileData = await axios ...
return profileData;
};
useEffect(() => {
try {
const data = fetchProfiles();
setProfiles(data); // SETS HERE
} catch (error) {
console.log(error);
}
}, []);
To this;
const fetchProfiles = async () => {
// const profileData = await axios ...
setProfiles(profileData); // SETS HERE
};
useEffect(() => {
try {
const data = fetchProfiles();
} catch (error) {
console.log(error);
}
}, []);
Imagine profileData is constant mock data. And you can try this at
Stackblitz link

Update a Table with React Hooks when a Row is Added, Deleted and Modified? [Issue: Gets Before Post and Delete]

I'm using Axios to get, put, and delete values from our database and have them displayed in a table; however, I need to refresh the page to see my changes. To find answers, I've visited these posts: How to update the page after call Axios Successful ? React, refresh table after action in react, and How can I use React Hooks to refresh a table when a row is deleted or added?
Unfortunately, I am still stuck and unsure how to dynamically update table rows upon response updates.
Update: I have noticed that the getValues function runs prior to the post and delete methods, which is why it is currently showing the previous values before the methods execute.
Axios.js - Where I am using get and delete methods. They work as I've had responses printed on the console.
import axios from "axios";
const getValues = async () => {
const values = await axios
.get("https://pokeapi.co/api/v2/type/")
.then((response) => {
return response.data;
})
.catch(function (error) {
console.log(error);
});
return values;
};
const postValues = (values) => {
axios
.post("https://pokeapi.co/api/v2/type/")
.then((response) => {
console.log("Post Values: ", response.data);
return response.data;
});
};
const deleteValues = (id) => {
console.log(id);
const deleteValues = axios
.delete(`https://pokeapi.co/api/v2/type/${id}`)
.then((response) => {
console.log("Delete Values: ", response);
})
.catch(function (error) {
console.log(error);
});
return deleteValues;
};
export { getValues, postValues, deleteValues }
ValuesTable.js - Where the delete method executes
import Axios from "./Axios";
const [data, setData] = React.useState();
useEffect(() => {
Axios.getValues().then((result) => {
setData(result.data);
});
}, [data]);
return (
{data.map((values) => {
<TableRow/>
<TableCell>{values.values}</TableCell>
<TableCell>
<Button
onClick={() =>
Axios.deleteValues(values.id);
}
/>
})};
)
Form.js - Where the post method executes
if (values.id === 0) {
Axios.postValues(values);
} else {
Axios.putValues(values, values.id);
}
UseState setData(result.data) loads all the existing values in the database.
Method deleteValues deletes a value in an array.
Method postValues adds a value into the database.
Well, you don't what to unconditionally call setData within an useEffect hook with data as a dependency as this will cause an infinite loop (render looping) to occur.
Since the getValues utility already unpacks the response.data value there is likely no need to do it again in your UI. Also, remove the data dependency.
useEffect(() => {
Axios.getValues()
.then((result) => {
setData(result.results);
});
}, []);
For the deleteValues utility, if console.log("Delete Values: ", response); is showing the correct values than I think you need to return this value from deleteValues.
const deleteValues = (id) => {
console.log(id);
const deleteValues = axios
.delete("https://pokeapi.co/api/v2/type/${id}`)
.then((response) => {
console.log("Delete Values: ", response);
return response; // <-- new data values
})
.catch(function (error) {
console.log(error);
});
return deleteValues;
};
Then in ValuesTable you need to update your data state with the new deleted values.
{data.map((values) => {
...
<Button
onClick={() => {
Axios.deleteValues(values.id)
.then(data => setData(data));
}}
/>
...
})};
Update
Ok, since the deleteValues utility doesn't return the updated data from the backend you will need to maintain your local state manually. I suggest doing this work in a callback handler. Upon successful deletion, update the local state.
const [data, setData] = React.useState();
useEffect(() => {
Axios.getValues().then((result) => {
setData(result.data);
});
}, []);
const deleteHandler = id => async () => {
try {
await Axios.deleteValues(id); // no error, assume success
setData(data => data.filter((item) => item.id !== id));
} catch(err) {
// whatever you want to do with error
}
};
return (
...
{data.map((values) => {
<TableRow/>
<TableCell>{values.values}</TableCell>
<TableCell>
<Button onClick={deleteHandler(values.id)}>
Delete
</Button>
})};
...
)
Note that I've written deleteHandler to be a curried function so you don't need an anonymous callback function for the button's onClick handler. It encloses the current id in an "instance" of the callback.
Update 2
If you are making a lot of different changes to your data in the backend it may just be easier to use a "fetch" state trigger to just refetch ("get") your data after each backend update. Anytime you make a call to update data in your DB, upon success trigger the fetch/refetch via a useEffect hook.
const [data, setData] = React.useState();
const [fetchData, setFetchData] = useState(true);
const triggerDataFetch = () => setFetchData(t => !t);
useEffect(() => {
Axios.getValues().then((result) => {
setData(result.data);
});
}, [fetchData]);
const deleteHandler = id => async () => {
try {
await Axios.deleteValues(id); // no error, assume success
triggerDataFetch(); // <-- trigger refetch
} catch(err) {
// whatever you want to do with error
}
};
I think you wrong in here :
useEffect(() => {
Axios.getValues().then((result) => {
setData(result.data); // result has no data property
});
}, [data]);
Please try change to this
useEffect(() => {
Axios.getValues().then((result) => {
console.log("RESULT",result); // check the actually response from API
setData(result.results); // the response of data is called results
});
}, [data]);
import axios from "axios"; const getValues = async () => { const values = await axios .get("https://pokeapi.co/api/v2/type/")
.then((response) => { return response.data; }) .catch(function (error)
{ console.log(error); }); return values; };
I don't know why but what are you trying to achieve with this. You should either use async/await clause or then clause but you are using both atleast have some good practice of coding first.
Second I think you should use async await inside try catch and remove then/catch phrases to make your code more understandable, then if you store your result inside values then simply return values.data and your problem might be resolved.
Since the deleteValues function deletes a specific object from the array on the server-side, I have decided to filter the list of objects in the array in order to remove the matching id to reflect on the front end. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
This is how I approached it.
{data.map((values) => {
...
<Button
onClick={() => {
setData(data.filter((item) => item.id !== values.id)); // <- Filter
Axios.deleteValues(values.id)
.then(data => setData(data));
}}
/>
...
})};

How to mock function, that is called in "then" in axios promise?

I have a function, that is fetching data from the backend. When fetch is successful, it extracts one value from the response, and then call another function (parseAllRecordsData), that is converting the value into other value. I'm trying to test this function, but after mocking parseAllRecordsData function, it's still trying to call original function (and throws errors from that function).
In other tests jest.fn or jest.spy is working correctly, but when I'm trying to mock function that is used in "then" it's not.
export function fetchAllRecordsData(payload) {
const url = `/apis/${payload.link.split('apis/')[1]}`;
return axios.get(url)
.then(({ data }) => {
if (data && data._embedded) {
const parsedData = data._embedded['taxonomies:entry'];
const arrayData = parseAllRecordsData(parsedData, payload);
return { data: List(arrayData) };
}
return { data: List([]) };
})
.catch((error) => ({ error }));
}
And my test:
describe('fetchAllRecordsData', () => {
const mockedPayload = {
link: 'apis/ok_link',
};
beforeAll(() => {
jest.spyOn(LegalListRecordsApi,'parseAllRecordsData').mockReturnValue(['test']);
});
it('test', async () => {
const test = await LegalListRecordsApi.fetchAllRecordsData(mockedPayload);
expect(test).toEqual(1);
});
});
When it's called like this, parseAllRecordsData calls real function, and throws the error, because mocked Axios response doesn't have some values that parsing function use. I'm only interested in return value, not calling this function.
jest.spyOn(LegalListRecordsApi,'parseAllRecordsData').mockReturnValue(['test']); mocks the module export for parseAllRecordsData.
This doesn't have any effect on fetchAllRecordsData because it is in the same module as parseAllRecordsData and is calling it directly.
ES6 modules supports cyclic dependencies so you can import a module into itself.
Import the module into itself and use the module to call parseAllRecordsData:
import * as LegalListRecordsApi from './LegalListRecordsApi'; // import module into itself
export function fetchAllRecordsData(payload) {
const url = `/apis/${payload.link.split('apis/')[1]}`;
return axios.get(url)
.then(({ data }) => {
if (data && data._embedded) {
const parsedData = data._embedded['taxonomies:entry'];
const arrayData = LegalListRecordsApi.parseAllRecordsData(parsedData, payload); // use the module
return { data: List(arrayData) };
}
return { data: List([]) };
})
.catch((error) => ({ error }));
}
...and the call will be mocked when you mock the module export for parseAllRecordsData.
export function fetchAllRecordsData(payload, axiosInterface = axios) {
return return axiosInterface.get(url)
. then(({ data }) => {
// your code
})
.catch((error) => ({ error }))
}
So, you need create mock object with method get, method get should return promise.

Calling one async function inside another in redux-thunk

I'm building a react app and use redux-thunk for async operations. I have two functions getActivities() and createActivity() and I want to call the former after successful calling the latter. But if I put getActivities() inside then block of createActivity() it simply isn't get called (which is proved by not seeing console.log() which I put in getActivities()). Here are both functions:
export const getActivities = () => dispatch => {
console.log('again');
return axios.get(ENV.stravaAPI.athleteActivitiesBaseEndPoint, autHeaders)
.then(resp => {
dispatch({type: actions.GET_ACTIVITIES, activities: resp.data})
})
.catch(err => {
if(window.DEBUG)console.log(err);
})
};
export const createActivity = data => dispatch => {
dispatch(setLoadingElement('activityForm'));
return axios.post(URL, null, autHeaders)
.then(resp => {
if (resp.status === 201) {
dispatch(emptyModal());
}
// I WANT TO CALL getActivities() HERE
dispatch(unsetLoadingElement('activityForm'));
})
.catch(err => {
if(window.DEBUG) console.log(err.response.data.errors);
dispatch(unsetLoadingElement('activityForm'));
});
};
How can I call one inside another?
In order to call another action from inside one action creator you just need to just dispatch the action like dispatch(getActivities())
export const createActivity = data => dispatch => {
dispatch(setLoadingElement('activityForm'));
return axios.post(URL, null, autHeaders)
.then(resp => {
if (resp.status === 201) {
dispatch(emptyModal());
}
dispatch(getActivities());
dispatch(unsetLoadingElement('activityForm'));
})
.catch(err => {
if(window.DEBUG) console.log(err.response.data.errors);
dispatch(unsetLoadingElement('activityForm'));
});
};
getActivites()
This does sucessfully call getActivities(). However, it returns an anonymous function which contains the console.log() call. You ignore this returned value here.
You must dispatch the returned function in order to ensure it is called:
dispatch(getActivities())

Categories

Resources