React and promise issues with fetch method - reactjs

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

Related

Pagination in React-Redux

So I'm just trying to make a pagination component in react. Im currently using redux for my state management and using semantic-ui for the pagination component.
I have currently made a react component in my action.jsx file and have two other functions which one of them is for data fetching for my redux state and one other for the declaring the current active page value and set the new target url for data fetching.
export class Paginator extends React.Component {
state = {
page: [],
pages: []
}
handlePage(activePage) {
let pagenum = activePage;
let pagestring = pagenum.toString();
paginationUrl = '/api/v1/products/index/?page=' + pagestring; ----> Pass This Url
}
componentDidMount() {
axios.get("/api/v1/products/index", { withCredentials: true })
.then(response => {
this.setState({
page: response.data.page,
pages: response.data.pages
})
})
.catch(error => {
console.log("Check Login Error", error);
});
}
render() {
return(
<Pagination onPageChange={this.handlePage} size='mini' siblingRange="6"
defaultActivePage={this.state.page}
totalPages={this.state.pages}
/>
)
}
}
export function fetchProducts() {
return (dispatch) => {
dispatch(fetchProductsRequest())
axios
.get("To Here !")
.then(response => {
// response.data is the products
const products = response.data.products
dispatch(fetchProductsSuccess(products))
})
.catch(error => {
// error.message is the error message
dispatch(fetchProductsFailure(error.message))
})
}
}
The question is how am i able to pass the paginationUrl to the function below ? (Actually, there is no way i guess !).
Note: I am only able to use handlePage in the same component with the pagination component.
Waiting for suggestions, Thx in advance ;)
You could pass the URL to the fetchProducts function when dispatching actions on page changes.
handlePage(activePage) {
const url = `/api/v1/products/index/?page=${activePage}`
dispatch(fetchProducts(url))
}
And update the fetchProducts action creator to use the URL.
export function fetchProducts(url) {
return (dispatch) => {
dispatch(fetchProductsRequest())
axios
.get(url)
.then((response) => {
dispatch(fetchProductsSuccess(response.data.products))
})
.catch((error) => {
dispatch(fetchProductsFailure(error.message))
})
}
}
This is unrelated to the question but I would strongly recommend using React Query to simplify data fetching and synchronization.

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 ;)

Jest/Enzyme/Reactjs testing function used by react component

Hi I have this function (apiCall) that calls an API inside a component and uses the data to update state (to then render a chart with chartjs). I want to test specifically the process inside componentDidMount that updates state without calling the API. After lots of time spent searching for a way of mocking this I still haven't been able to figure it out. Trying to assert the changed state from a mock apiCall function.
this is the apiCall function:
const apiCall = (uri) => {
return fetch(uri)
.then( (res) => {
return res
})
.catch( (ex) => {
return 0
})
}
export default apiCall;
// and this is the componentDidMount
componentDidMount() {
apiCall(this.props.uri)
.then((result) => result.json())
.then((result) => {
this.setState({ data: result });
})
this.setState({ legend: this.props.legend })
}
One of the options is to use fetch-mock
http://www.wheresrhys.co.uk/fetch-mock/
Use proxyquire and mock promise function

React, The function does not load data

How to rewrite the function so that it is updated and loaded every time you change pages. The fact is that the loading function works only on one page, but it does not pass to others, how to change it?
function loadModel(model) {
return function(dispatch) {
dispatch(moveToPending(model))
const resource = require(`../resources/${model}`)
const resourceActions = bindActionCreators(resource.actions, dispatch)
const toaster = new Toaster(dispatch)
return new Promise((resolve, reject) => {
resourceActions[getFunctionName(model)]()
.then(res => {
resolve(model)
dispatch(resolveSubscriptions(model))
})
.catch(err => {
if (debug) console.error(err)
reject({ ...err, model })
dispatch(resolveSubscriptions(model))
toaster.error(`Could not load ${model}!`)
})
})
}
}
Update.
Here's the componentWillMount(), I already have it, what do I need to add to it?
componentWillMount() {
this.props.actions.subscribe(this.subscriptions)
.then(() => {
this.props.actions.fetchRegisters({year: this.state.currentYear, month: defaultMonths()})
.then(() => {
if (!this.props.registers.length) {
this.toaster.warning('There is no data for charts')
}
this.createReportState()
})
})
}
React has some lifecycle methods. You can use componentWillMount or componentDidMount for this purpose. You can pass this function as a prop to other pages and there you can call it in componentWillMount, something like:
componentWillMount() {
this.props.loadModel(//arg);
}
For reference: Component life-cycle methods

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