Can't retrieve the data from this API - javascript

I have developed this live search component in react which retrieves data from an API according to the input search value. However it doesn't retrieve or display the data when pointed to this API https://api.itbook.store/1.0/search/program
But when i use an API like for example: http://hn.algolia.com/api/v1/search?query=redux it retrieves data
const [data, setData] = useState({ books: [] });
const [query, setQuery] = useState('program');
const [url, setUrl] = useState(
'https://api.itbook.store/1.0/search/program',
);
useEffect(() => {
const fetchData = async () => {
const result = await axios(url);
setData(result.data);
};
fetchData();
}, [url]);
return(
<Paper className={classes.root}>
<Container maxWidth="lg">
<form className={classes.container} encType="multipart/form-data">
<TextField
required
id="standard-required"
placeholder="Enter Book Name"
label="Search for a Book"
name="bookName"
value={query}
onChange={event => setQuery(event.target.value)}
className={classes.textField}
multiline
rowsMax="2"
margin="normal"/>
<Button onClick={() =>
setUrl(`https://api.itbook.store/1.0/search/${query}`)
}
className={classes.button} color="primary">Search</Button>
<ul>
{data.books.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
</form>
</Container>
</Paper>
I want my code to collect the data from the API Json : https://api.itbook.store/1.0/search/something

#sideshowbarker delivers an excellent solution to this problem Trying to use fetch and pass in mode: no-cors
Essentially what you can do to workaround the CORS issue is make your request via CORS Proxy URL.
Here's a working sandbox with updates to your code:
https://codesandbox.io/s/lucid-kapitsa-w1uid
import React, { useState } from "react";
import ReactDOM from "react-dom";
import axios from "axios";
import "./styles.css";
const App = () => {
const [url, setUrl] = useState("https://api.itbook.store/1.0/search/");
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);
const fetchData = async () => {
const proxyURL = "https://cors-anywhere.herokuapp.com/";
const updatedURL = `${proxyURL}${url}${query}`;
const res = await axios(updatedURL);
setResults(res.data.books);
};
const createBookList = () => {
return results.map(book => {
return (
<div>
<h4>{book.title}</h4>
</div>
);
});
};
return (
<div>
<input onChange={e => setQuery(e.target.value)} value={query} />
<button onClick={fetchData}>Click</button>
<div>{createBookList()}</div>
</div>
);
};

Related

How to reset every input data which we've rendered on every input in react js?

import React, { useState } from "react";
import axios, { Axios } from "axios";
import {
ContainerDiv,
InnerDiv,
StyledButton,
StyledInput,
} from "./StyledComponents";
function WeatherCard() {
const [input, SetInput] = useState("");
const [apiData, setApiData] = useState([]);
const [temp, setTemp] = useState([]);
const [minTemp, setMinTemp] = useState([]);
const [maxTemp, setMaxTemp] = useState([]);
const [pressure, setPressure] = useState([]);
const [humidity, setHumidity] = useState([]);
const [error, setError] = useState([]);
const [wind, setWind] = useState([]);
const handleOnChange = (e) => {
SetInput(e.target.value);
console.log(input);
};
const handleButtonClick = () => {
axios
.get(
`https://api.openweathermap.org/data/2.5/weather?q=${input}&appid=0f5ee70dcdfa521f9fbd0cacb97d71b7`
)
.then((data) => {
setApiData(data.data.weather);
setTemp(Math.round(data.data.main.temp - 273));
setMinTemp(Math.round(data.data.main.temp_min - 273));
setMaxTemp(Math.round(data.data.main.temp_max - 273));
setPressure(data.data.main.pressure);
setHumidity(data.data.main.humidity);
setWind(data.data.wind.speed);
})
.catch((e) => {
setError(`${input} City not found.`);
});
};
return (
<>
<ContainerDiv>
<InnerDiv>
<label for="city">City: </label>
<StyledInput
type="text"
value={input}
onChange={handleOnChange}
name="city"
></StyledInput>
<StyledButton onClick={handleButtonClick}>
Get Weather Data
</StyledButton>
</InnerDiv>
</ContainerDiv>
<ContainerDiv>
<div>
{apiData.map((item) => {
return <p>Weather: {item.description}</p>;
})}
<p>Temperature: {temp}°C</p>
<p>Minimum Temperature: {minTemp}°C</p>
<p>Maximum Temperature: {maxTemp}°C</p>
<p>Pressure: {pressure}pa</p>
<p>Humidity:{humidity}%</p>
<p>Wind Speed: {wind}</p>
<p>Error: {error}</p>
</div>
</ContainerDiv>
</>
);
}
export default WeatherCard;
"in this code the last results are there if we get any error i.e. by typing wrong city name, the problem is that the last data is still there and there is error which we cant tell if its for new input or last input. please dont mind my english as it is my third language"
i want to reset rendered data on every input.
just set your states to an empty string after getting an error
.catch((e) => {
setError(`${input} City not found.`);
setApiData([]);
setTemp('');
setMinTemp('');
setMaxTemp('');
setPressure('');
setHumidity('');
setWind('');
}

How To Render Data From Firebase Without Page Refresh

I'm creating a simple note taking app. The functionality I need is the ability to make a new LABEL, and then inside that label create a TITLE, with TAGS(notes) associated to that title. Right now I have it all working nicely, except when I submit the new label, and/or new title/tags, I need to refresh to see the newly added data. I've used tutorials as well as trial and error to get to this point, and I'm a newly lamented bootcamp graduate so please be patient with me :) Any advice is greatly appreciated. How can I make it so when I "createLabel" or "createCard", the data is instantly rendered on screen instead of having to refresh my page?
Here's the code:
import "./App.css";
import { useState, useEffect } from "react";
import React from "react";
// Components
import Header from "./components/Header/Header";
// Firebase
import { db } from "./firebase-config";
import {
collection,
getDocs,
getDoc,
setDoc,
doc,
updateDoc,
addDoc,
deleteDoc,
arrayUnion,
} from "firebase/firestore";
// Clipboard
import { CopyToClipboard } from "react-copy-to-clipboard";
function App() {
const [newLabel, setNewLabel] = useState("");
const [labels, setLabels] = useState([]);
const [activeLabels, setActiveLabels] = useState("");
const [name, setName] = useState("");
const [tags, setTags] = useState("");
const labelCollectionRef = collection(db, "labels");
--- I think I need to add logic to the useEffect maybe?
useEffect(() => {
const getLabels = async () => {
const data = await getDocs(labelCollectionRef);
setLabels(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
getLabels();
}, []);
const createLabel = async () => {
await setDoc(doc(db, "labels", newLabel), { id: newLabel });
};
const clickHandler = async (title) => {
setActiveLabels(title);
};
const createCard = async () => {
await updateDoc(doc(db, "labels", activeLabels), {
info: arrayUnion({ name: name, tags: tags }),
});
};
// const deleteRef = doc(db, "labels", `${doc.data.id}`)
const deleteLabel = async (i) => {
await deleteDoc(doc(db, "labels", activeLabels));
};
return (
<div className="App">
<Header />
<main>
<input
onChange={(e) => {
setNewLabel(e.target.value);
}}
placeholder="Enter Label Name"
type="text"
></input>
<button onClick={createLabel}>Create Label</button>
{labels.map((label, i) => {
if (activeLabels == label.id) {
return (
<>
<div className="tags__section">
<h2
key={i}
className="tags__title"
onClick={() => clickHandler(label.id)}
>
Label: {label.id}
<button
onClick={() => deleteLabel(i)}
className="tags__delete"
>
DELETE
</button>
</h2>
</div>
{label.info &&
label.info.map((info, i) => {
return (
<div key={i}>
<h1 className="tags__name">Name: {info.name}</h1>
<h2 className="tags__tags" type="text">
Tags: {info.tags}
</h2>
<CopyToClipboard text={info.tags}>
<button className="tags__clipboard">COPY</button>
</CopyToClipboard>
</div>
);
})}
<div className="tags__add">
<input
onChange={(e) => setName(e.target.value)}
type="text"
name="title"
placeholder="Add title..."
></input>
<textarea
onChange={(e) => setTags(e.target.value)}
name="tags"
type="text"
placeholder="Add tags..."
></textarea>
<button onClick={createCard}>Create Card</button>
</div>
</>
);
} else {
return (
<h2
className="tags__title"
onClick={() => clickHandler(label.id)}
key={i}
>
Label: {label.id}
</h2>
);
}
})}
</main>
</div>
);
}
export default App;
There are two ways to retrieve data stored in the Cloud Firestore. What you're using is getDocs() method which is a method to get the data once. The second one is onSnapshot(); this sets a listener to receive data-change events which means Cloud Firestore will send your listener an initial snapshot of any changes to your documents, collections of documents, or the results of queries. See sample code below on how to implement the onSnapshot() method:
useEffect(() => {
const getLabels = async () => {
const colRef = query(collection(db, '<collection-name>'))
onSnapshot(colRef, (snapshot) => {
setLabels(snapshot.docs.map(doc => ({
id: doc.id,
data: doc.data()
})))
})
}
getLabels()
}, [])

There's a bug with Search and Pagination in React

I'm building my site in React and I have created pagination and search. When I search for something on the site, it only works when after that I go to another page. I think this is due to the fact that Softwares and Pagination are in the same component.
Then I tried lifting-state-up, but I got an error: React Minified Error # 31.
Here's Pagination component:
const Paginator = ({
total, // Total records
startPage = 1,
totalPages = null,
onMovePage = null,
}) => {
...
return (
<>
<section id={styles.paginator}>
<Header/>
...
{range(1, totalPages+1).map(p => (
<PagItem key={p} handleClick={ () => {setCurrentPage(p); onMovePage && onMovePage({currentPage: p})} } title={p} name={p} />
))}
...
</section>
</>
);
};
Here's Softwares component:
const Softwares = ({ search }) => {
const [softwares, setSoftwares] = useState([]);
const [total, setTotal] = useState(null);
const [totalPages, setTotalPages] = useState(null);
const [valid, setValid] = useState(false);
const fetchData = async ({ currentPage }) => {
const SEARCH = search ? `?search=${search}` : '';
const CURRENT_PAGE = currentPage && SEARCH === '' ? `?page=${currentPage}` : '';
const response = await fetch(`http://127.0.0.1:8000/api/software/${CURRENT_PAGE}${SEARCH}`);
const data = await response.json();
setSoftwares(data.results);
setTotal(data.count);
setTotalPages(data.total_pages);
setValid(true);
}
useEffect(() => {
fetchData({ currentPage: 1 });
}, []);
return (
<>
{
valid &&
<section className={styles.softwares}>
<Header header={"new softwares"} />
{softwares.map(s => (
<Article key={s.id} pathname={s.id} title={s.title} image={s.image} pubdate={s.pub_date} icon={s.category.parent.img} categoryID={s.category.id} categoryName={s.category.name} dCount={s.counter} content={s.content} />
))}
<Paginator totalPages={totalPages} total={total} onMovePage={fetchData} />
</section>
}
</>
);
};
SearchForm in Header component:
const Header = ({ handleChange, handleClick }) => {
return (
...
<SearchForm handleChange={handleChange} handleClick={handleClick} />
...
);
};
const SearchForm = ({ style, handleChange, handleClick }) => {
return (
<div style={style}>
<form>
<input
type="text"
onChange={handleChange}
/>
<SearchButton onClick={handleClick} />
<small>ENTER</small>
</form>
</div>
);
};
const SearchButton = ({onClick }) => {
return (
<button type="button" onClick={onClick}>
<FontAwesomeIcon icon={faSearch} />
</button>
);
};
And part of Search in App component:
const App = () => {
...
// Search
const [search, setSearch] = useState('');
const [shouldFetch, setShouldFetch] = useState(false);
const handleChange = (e) => {
setSearch(e.target.value);
}
useEffect(() => {
if (shouldFetch) {
(async () => {
const response = await fetch(`http://127.0.0.1:8000/api/software/?search=${search}`);
const data = await response.json();
setShouldFetch(false);
})()
}
}, [shouldFetch]);
const handleClick = () => setShouldFetch(true);
return (
<div className="App">
<Header handleChange={handleChange} handleClick={handleClick} />
...
<Switch>
<Route path="/" exact render={props => <Softwares {...props} search={search} />} />
</Switch>
{/* Actually I'd like to use Paginator here, but it
throws the error: React Minified Error # 31 */}
...
</div>
);
}
So, how can this be done?
The problem is your useEffect dependencies (or lack thereof).
Here's the relevant section of the code:
const Softwares = ({ search }) => {
const [softwares, setSoftwares] = useState([]);
const [total, setTotal] = useState(null);
const [totalPages, setTotalPages] = useState(null);
const [valid, setValid] = useState(false);
const fetchData = async ({ currentPage }) => {
const SEARCH = search ? `?search=${search}` : '';
const CURRENT_PAGE = currentPage && SEARCH === '' ? `?page=${currentPage}` : '';
const response = await fetch(`http://127.0.0.1:8000/api/software/${CURRENT_PAGE}${SEARCH}`);
const data = await response.json();
setSoftwares(data.results);
setTotal(data.count);
setTotalPages(data.total_pages);
setValid(true);
}
useEffect(() => {
fetchData({ currentPage: 1 });
}, []);
The empty dependency array means that you are running the effect that calls fetchData one time when the component mounts. Clicks in the Pagination component will call the fetchData function directly. Changes to search do not cause fetchData to re-run. The data depends on the search so search should be a dependency.
The fetchData function is fine in this component. The state that I would recommend lifting up is to lift the currentPage up from Pagination into Softwares. The onMovePage callback can just update the currentPage state. That way you can call fetchData only through your effect and run the effect whenever either search or currentPage changes.
const Softwares = ({ search }) => {
const [softwares, setSoftwares] = useState([]);
const [total, setTotal] = useState(null);
const [totalPages, setTotalPages] = useState(null);
const [valid, setValid] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
useEffect(() => {
// defining the function inside of the useEffect
// lets eslint exhaustive dependency checks work their magic
const fetchData = async () => {
const SEARCH = search ? `?search=${search}` : '';
const CURRENT_PAGE = currentPage && SEARCH === '' ? `?page=${currentPage}` : '';
const response = await fetch(`http://127.0.0.1:8000/api/software/${CURRENT_PAGE}${SEARCH}`);
const data = await response.json();
setSoftwares(data.results);
setTotal(data.count);
setTotalPages(data.total_pages);
setValid(true);
}
// need to define and call in separate steps when using async functions
fetchData();
}, [currentPage, search]);
return (
...
<Paginator page={currentPage} totalPages={totalPages} total={total} onMovePage={setCurrentPage} />
...
);
};

save data using hooks

I fetch data from goodreads API and save it in books array using hooks.
I need to get data and display in component according to search query. but there is an issue in console (Uncaught TypeError: setSearchText is not a function)
function App() {
const [books, setBooks] = useState([])
const [searchText, setSearchText] = ('')
const key = "xxx"
const url = `https://cors-anywhere.herokuapp.com/` +
`https://www.goodreads.com/search/index.xml?key=${key}&q=${searchText}`;
useEffect (()=> {
loadData()
}, [])
const loadData = async () => {
axios.get(url).then((res) => {
setBooks(res.data)
console.log(books)
})
}
const onTextChange = (e) => {
setSearchText(e.target.value)
}
return (
<>
<Container>
<h1>Bookstore</h1>
<input type='text'
placeholder='Search...'
name="searchText"
onChange={onTextChange}
value={searchText}
/>
</Container>
</>
);
}
You are missing a function call on the right side of the destructuring assignment.
const [books, setBooks] = useState([])
const [searchText, setSearchText] = ('') // This should be useState('')

Rendering info from the Reddit API using React

I am trying to build a basic web app where a user can search for subreddits using the Reddit API.
However, while I can console.log the data I need from the API I cannot seem to figure out how to display it.
import React, { useState, useEffect } from "react";
import Amplify, { API, graphqlOperation } from "aws-amplify";
import aws_exports from "./aws-exports";
import { withAuthenticator, AmplifySignOut } from '#aws-amplify/ui-react';
import awsconfig from "./aws-exports";
import './App.css'
import axios from 'axios'
import CharacterGrid from './components/CharacterGrid'
import SearchBar from './components/SearchBar'
Amplify.configure(awsconfig);
const App = () => {
const [items, setItems] = useState([])
const [isLoading, setIsLoading] = useState(true)
const [query, setQuery] = useState('')
useEffect(() => {
const fetchItems = async () => {
setIsLoading(true)
const result = await axios(
`https://www.reddit.com/subreddits/search.json?q=${query}`
)
setItems(result.data)
setIsLoading(false)
}
fetchItems()
}, [query])
return (
<div className='container'>
<AmplifySignOut/>
<SearchBar style={{ marginTop: "6%" }} getQuery={(q) => setQuery(q)} />
<CharacterGrid isLoading={isLoading} items={items} />
</div>
)
}
export default withAuthenticator(App)
The child component CharacterGrid looks like this:
import React from 'react'
import CharacterItem from './CharacterItem'
const CharacterGrid = ({items, isLoading}) => {
return
isLoading
? (<h1>Loading ...</h1>)
: (
<section className='cards'>
<p>{items.data}</p> //this does not work
<p>{items.children}</p> //this does not work either
</section>
)
}
export default CharacterGrid
All I want to do is show the names of the subreddits that are returned from the API for the query string the user enters. Where am I going wrong? I have also tried converting to JSON, and mapping through the responses using .map() but I keep getting errors no matter what I try. Where am I going wrong?
However, while I can console.log the data I need from the API I cannot seem to figure out how to display it.
Because Reddit API returns an array of subreddits you should use map() function to iterate over the array and convert each item into React element.
items.map(i => (
<li key={i.data.display_name_prefixed}>
{i.data.display_name}
</li>
))
All I want to do is show the names of the subreddits that are returned from the API for the query string the user enters.
You need to inspect the data schema yourself and scrape the response properly.
Here is working example:
const { useState, useEffect } = React;
const App = () => {
const [items, setItems] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [query, setQuery] = useState("");
useEffect(() => {
setIsLoading(true);
if (query.length >= 3) {
axios(`https://www.reddit.com/subreddits/search.json?q=${query}`)
.then(response => setItems(response.data.data.children))
.catch(error => console.log("Error", error));
}
setIsLoading(false);
}, [query]);
return (
<div>
<input type="text" value={query} onChange={e => setQuery(e.target.value)} />
<CharacterGrid items={items} isLoading={isLoading} />
</div>
);
}
const CharacterGrid = ({ items, isLoading }) => {
return isLoading ? (
<h1>Loading ...</h1>
) : (
<ul>
{items.map(i => (
<li key={i.data.display_name_prefixed}>
{i.data.display_name} ({i.data.display_name_prefixed})
</li>
))}
</ul>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script>
<div id="root"></div>
In order to loop through the elements and e.g: print the title, you could do the following.
items.data.children.map(child => <p> child.data.title </p>)
According to the response provided by reddit,
reddit response

Categories

Resources