Multiple API handling through Axios in react application - javascript

When I have Two APIs and one for cats data and one for cats image, how can I populate images and data from two APIs in one component using hooks through Axios call?
CodeSandbox
CatsList.js
import React, {useState, useEffect} from 'react'
import {baseUrl} from './services/mainApi';
import axios from 'axios';
export default function CatsList() {
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = async () => {
const result = await axios(
`${baseUrl}breeds`,
);
setData(result.data);
};
fetchData();
}, []);
return (
<ul className="row">
{data.map(item => (
<li className="col-md-3 list-item" key={item.id}>
<a href={item.wikipedia_url}>
<h2>{item.name}</h2>
<p>{item.origin}</p>
<p>{item.description}</p>
</a>
</li>
))}
</ul>
);
}
mainApi.js
import axios from 'axios';
export const baseUrl = 'https://api.thecatapi.com/v1/'
export const catsImage = `${baseUrl}/images/search?breed_id=abys`

You'll need to make a a request per cat to get the cats image, before setting data e.g.
const fetchData = async () => {
const result = await axios(
`${baseUrl}breeds`,
);
const catImageResponses = Promise.all(
result.data.map(cat => axios(buildCatImageUrl(cat)))
)
// handle catImageResponses, correlating them with result.data (cats), then set state
};
fetchData();
You'll need to correlate your cat images with your cats before setting your state, and you'll need to build the cat image url dynamically based on the cat, at the minute its hardcoded.

Related

How to get an image from Directus?

I need to display images from directus
import React, { useEffect, useState } from 'react'
import { fetchArticles } from './async/fetchArticels'
const FileUpload = () => {
const [articles, setArticles] = useState([])
useEffect(()=>{
fetchArticles().then(data => setArticles(data))
}, [])
return (
<div>
{articles.map(article =>
<div>
<h3>{article.title}</h3>
<img src={article.image} alt="img" />
</div>
)}
</div>
)
}
export default FileUpload
code
import axios from "axios"
export const fetchArticles = async () => {
const {data} = await axios.get('http://localhost:8055/items/articles')
console.log(data.data)
return data.data
}
from the directus I get this data
I read about the blob method, but I can't get it.
What should I do?
From the Directus Docs:
You can consistently access [your files/images] via the API using the following URL.
example.com/assets/<file-id>
example.com/assets/1ac73658-8b62-4dea-b6da-529fbc9d01a4
Reference: https://docs.directus.io/reference/files/#accessing-an-file
For You
As you're wishing to display images in the browser, you will likely want something like this.
<img src={"//example.com/assets/" + article.image}" alt={article.title} />

How can I update state after setting it in UseEffect?

I want to display a list of products in one of my components. I need to use UseEffect to get the results of a function that returns a promise. I then am setting state in UseEffect but am having trouble displaying the products. The state is getting updated but I am not able to render the results on the dom.
Here's the component:
import React, { useEffect, useState } from "react"
import styled from "styled-components"
import pullShopifyData from "../../functions/shopify"
export default function DataPreview(props)
const { apiKey } = props
const [data, setData] = useState([])
useEffect(() => {
pullShopifyData(apiKey).then(response => {
console.log(response.data.shop.products)
setData(response.data.shop.products)
})
}, [])
return (
<Wrapper>
{data.length > 0 ? data.map(product => (
<ProductWrapper>{product}</ProductWrapper>
)): <NoProductsWrapper>No Products Found</NoProductsWrapper>}
</Wrapper>
)
}
const Wrapper = styled.div``
const ProductWrapper = styled.div``
const NoProductsWrapper = styled.div``
Here's the output on the dom:
Move your data fetch function in to async wrapper function. Then just call it in useEffect.
useEffect(() => {
fetchSomething();
}, [])
const fetchSomething = async () => {
const response = await pullShopifyData(apiKey);
console.log(response.data.shop.products)
setData(response.data.shop.products)
}

Adding async before functional component causes it to return an object?

I have an App component and BookList component, which is rendered in App. When I add async to my BookList component I get the following error:
Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.
in BookList (at App.js:18)
in div (at App.js:16)
in ApolloProvider (at App.js:15)
in App (at src/index.js:16)
in StrictMode (at src/index.js:14)
App:
import React from 'react';
import styled from 'styled-components';
import BookList from './components/BookList';
import ApolloClient from 'apollo-boost';
import { ApolloProvider } from '#apollo/react-hooks';
export const client = new ApolloClient({
uri: 'http://localhost:4001/graphql',
})
const App = () => {
return (
<ApolloProvider client={client}>
<div id='main'>
<h1>Javed's Reading List</h1>
<BookList />
</div>
</ApolloProvider>
);
}
export default App;
BookList:
const BookList = async () => {
const query = gql`
query {
books {
name
id
}
}
`;
const { data } = await client.query({ query })
return (
<div id='main'>
<ul id='book-list'>
<li>Book Name</li>
</ul>
</div>
);
};
export default BookList;
I understand adding async causes the return value to be a promise, but I've done this in the past without any issues. I'm not sure what I'm missing. Why can't I render BookList?
EDIT:
I followed the advice of #MorKadosh and the useEffect hook is not firing on page load
const BookList = () => {
const [data, setData] = useState(null)
useEffect(() => {
const fetch = async () => {
const response = await client.query({ query });
console.log(response.data);
console.log('hello')
setData(response.data);
}
fetch();
}, [])
return (
<div id='main'>
<ul id='book-list'>
<li>Book Name</li>
</ul>
</div>
);
};
export default BookList;
You shouldn't do that.
Instead, use useEffect and useState hooks to fetch your data:
const [data, setData] = useState(null);
useEffect(() => {
const query = ...
const fetch = async () => {
const response = await client.query({ query });
setData(response.data);
};
fetch();
}, []);
This is just example, adjust it for your own needs.
Why you shouldn't do that? because as you said correctly, using async will always return a Promise which is not a valid react component.
I think that happens the promise doesn't match with react interface
Why didn't you wrap your API call in an async function or use the useEffect hook?
const apiCall = async () => await client.query(...)

How can I use get values from array of objects from JSON response

I am trying to learn how to use API's in react. I am making a search input for country names using the Rest countires API. I am getting data from https://restcountries.eu/rest/v2/all but I do not know how to handle this data as I can not use map on an object.
import axios from "axios";
import React, { useEffect, useState } from "react";
const App = () => {
const [countries, setCountries] = useState([]);
const [searchName, setSearchName] = useState("");
useEffect(() => {
axios.get("https://restcountries.eu/rest/v2/all").then(response => {
setCountries(response.data);
});
}, []);
const handleSearch = event => {
setSearchName(event.target.value);
};
return (
<div>
<div>
find countries <input onChange={handleSearch} />
</div>
<div></div>
</div>
);
};
export default App;
Expected to list countries after typing such as : sw = Botswana, Swaziland, Sweden ...
From the question it seems like, these are requirements of your app -
1
you need to search by country name
As you type in, list of countries matching the search should be displayed.
I created this sandbox with the code you provided - https://codesandbox.io/embed/58115762-rest-countries-o638k.
It shows a pair of country name and its capital as you enter input in the search box.
This is how I changed your code:
You need to search countries? - Use search API with country name as value of text input - searchName
https://restcountries.eu/rest/v2/name/${searchName}
To display the output with countries matching your search keyword - map over countries and get appropriate keys. Pass those keys as props to your newly created Country component.
Note, I did not need to change how you handled the JSON response. The searchName and countries are the only two state variables used to render the UI.
you will need to render countries after fetching from ajax request as like :
import axios from "axios";
import React, { useEffect, useState } from "react";
const App = () => {
const [countries, setCountries] = useState([]);
const [searchName, setSearchName] = useState("");
useEffect(() => {
axios.get("https://restcountries.eu/rest/v2/all").then(response => {
setCountries(response.data);
});
}, []);
const handleSearch = event => {
setSearchName(event.target.value);
};
return (
<div>
<div>
find countries <input onChange={handleSearch} />
</div>
<ul>
{(countries.length<=0)?"":
countries.map(country=> <li>country.name</li> )
}
</ul>
</div>
);
};
export default App;
I think this is what you are looking for.
If you have got questions, dont hesitate to ask :)
import axios from "axios";
import React, { useEffect, useState } from "react";
const App = () => {
const [countries, setCountries] = useState([]);
const [searchName, setSearchName] = useState("");
useEffect(() => {
axios.get("https://restcountries.eu/rest/v2/all").then(response => {
setCountries(response.data);
});
}, []);
const handleSearch = event => {
let str = event.target.value;
let filteredCountries = countries.filter((country) => country.name.toLowerCase().includes(str.toLowerCase()));
setCountries(filteredCountries);
setSearchName(str);
};
return (
<div>
<div>
find countries <input onChange={handleSearch} />
</div>
<ul> {(countries.length <= 0) ? "" : countries.map(country => <li>{country.name}</li>) } </ul>
</div>
);
};
export default App;
data =[your array];
countryList = data.map(data=>data.name)
console.log(countryList)

Easy Peasy state managment - How to fetch and pass data correctly?

I am using Easy Peasy State management for React. I would like to create multiple Axios call from one store location and import it in each page there where I need to show the correct data. I am trying to fetch a JSON placeholder data for example and use that inside a component to push it to the state using Hooks.
But I get the following error:
model.js:14 Uncaught (in promise) TypeError: actions.setTodos is not a function
at model.js:14
Can someone help me out? What am I doing wrong?
My code for the store (model.js):
import { thunk } from 'easy-peasy';
export default {
todos: [],
fetchTodos: thunk(async actions => {
const res = await fetch(
'https://jsonplaceholder.typicode.com/todos?_limit=10'
);
const todos = res.json();
actions.setTodos(todos);
}),
};
My Page component Contact:
import React, { useState, useEffect } from 'react';
import { useStoreActions } from 'easy-peasy';
import ReactHtmlParser from 'react-html-parser';
import { API_URL } from 'constants/import';
// import axios from 'axios';
const Contact = () => {
const [contactPage, setContactPage] = useState([]);
const { page_title, page_content, page_featured_image } = contactPage;
const fetchTodos = useStoreActions(actions => actions.fetchTodos);
useEffect(() => {
fetchTodos();
}, []);
return (
<section className="contact">
<div className="page">
<div className="row">
<div className="col-xs-12">
<h3 className="section__title">{page_title}</h3>
{ReactHtmlParser(page_content)}
{page_featured_image && (
<img src={API_URL + page_featured_image.path} />
)}
</div>
</div>
</div>
</section>
);
};
export default Contact;
You need to use action.
import { action, thunk } from "easy-peasy";
export default {
fetchTodos: thunk(async (actions, payload) => {
const res = await fetch(
"https://jsonplaceholder.typicode.com/todos?_limit=10"
);
const todos = res.json();
actions.setTodos(todos);
}),
todos: [],
setTodos: action((state, payload) => {
console.log("---->>> payload!")
state.todos = payload
}),
};
I usually use it like this, it works perfectly for me.

Categories