I started learning React about 3 or 4 months ago. Initially went through Codecademy's course, and have since polished up a lot with other courses and tutorials, the main of which used Axios for get requests without really explaining Axios. I decided to go back to the "Ravenous" yelp clone project at Codecademy, the instructions of which are for class components, and thought it would be a nice challenge to complete that same project but with functional components. Everything was perfect until it was time to wire up the Yelp api, and given my lack of knowledge with axios and requests in general, I can't pinpoint what I'm doing wrong. I'm almost certain that I'm doing something wrong in Yelp.js, but I can't be sure because I'm getting a type error that "fetchRestaurants" is not a function in App.js, no matter which way I call it. I think it involves Promises, and I tried using async/await on searchYelp, but this part is all new to me.
Here's Yelp.js:
import axios from "axios";
const KEY = "RwNYGeKdhbfM1bo6O8X04nLNR7sKjIXyAQ-Fo-nz-UdNHtSKDAmHZCc9MSiMAVmNhwKrfcukZ0qlHF1TdVIRSrgak3-oHVNmGD5JPR4ysxhfGd1hgOllt83H1i44X3Yx";
const fetchRestaurants = (term, location, sortBy) => { const corsApiUrl = "https://cors-anywhere.herokuapp.com/"; return () => {
axios
.get(
`${corsApiUrl}https://api.yelp.com/v3/businesses/search?categories=restaurants&location=${location}&term=${term}&sort_by=${sortBy}`,
{
headers: {
Authorization: `Bearer ${KEY}`,
},
}
)
.then(res => res.data.businesses)
.catch(error => console.log(error.response)); }; };
export default fetchRestaurants;
And here's App.js:
import React, { useState } from "react";
import BusinessList from "./BusinessList";
import SearchBar from "./SearchBar";
import Yelp from "./Yelp";
import "./App.css";
const App = props => {
const [businesses, setBusinesses] = useState([]);
const searchYelp = (term, location, sortBy) => {
const businessList = Yelp.fetchRestaurants(term, location, sortBy);
setBusinesses(businessList);
};
return (
<div className="App">
<h1>Ravenous</h1>
<SearchBar searchYelp={searchYelp} />
<BusinessList businesses={businesses} />
</div>
);
};
export default App;
Apologies in advance if this was supposed to be posted somewhere else. Noob probs.
you did not save the response anywhere. .then(res => res.data.businesses) here the response should've been saved somewhere, either a varibale or state. also your function responseble for the axios request doesn't return anything.
It's not an issue with axios itself, but how you use it in your React application.
Yelp.js: fetchRestaurants() should return a proper value (promise in this case) :
const fetchRestaurants = (term, location, sortBy) => {
return axios
.get(url, options)
.then(res => res.data.businesses)
.catch(...)
}
App.js: All API calls should be done using useEffect hook:
useEffect(() => {
Yelp.fetchRestaurants(term, location, sortBy)
.then(businessList => setBusinesses(businessList));
}, [term, location, sortBy]);
Related
I am learning Redux and I have deviated from the instructor's code. I am trying to convert my code from context & state into Redux.
Is it advisable to use setReduxObject (setCategoriesMap in my code) and selectReduxObject (selectCategoriesMap in my code) in the same .jsx page? Are there any concerns around this?
Thanks!
My code:
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { getCategoriesAndDocuments } from "../../utils/firebase/firebase.utils";
import { setCategoriesMap } from "../../store/categories/categories.action";
import { selectCategoriesMap } from "../../store/categories/categories.selector";
import Category from "../../components/category/category.component";
const Shop = () => {
const dispatch = useDispatch();
useEffect(() => {
const getCategoriesMap = async () => {
const categories = await getCategoriesAndDocuments();
dispatch(setCategoriesMap(categories));
};
getCategoriesMap();
}, []);
const categoriesMap = useSelector(selectCategoriesMap);
return (
<div>
{Object.keys(categoriesMap).map((key) => {
const products = categoriesMap[key];
return <Category key={key} title={key} products={products} />;
})}
</div>
);
};
export default Shop;
This is just the default approach, nothing to be concerned about.
As soon as you're using getCategoriesAndDocuments the same way in another component though, it's better to move this to an async action creator.
Could even do it for this component already to improve separation of concerns. The component does not necessarily need to be involved with firebase, its job is display logic. Wether the data comes from firebase or localStorage or some graphQL server should not matter.
I’m building a Django-React application (Django API on its standalone server on port 8000 and React front-end on its standalone server on port 3000) using axios to fetch data from the Django server and Redux to dispatch the data. So, I created the redux action like this:
// GET TRANSACTIONS: /action/transactions.js
import axios from 'axios';
import { GET_TRANSACTIONS } from './types';
export const getTransactions = () => (dispatch) => {
axios.get('http://127.0.0.1:8000/api/transaction')
.then((res) => {
dispatch({
type: GET_TRANSACTIONS,
payload: res.data,
});
}).catch((err) => console.log(err));
};
And in the the React view to get the data from redux store I have this:
// React Views :
import React, {Component,} from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { getTransactions } from '../../../../actions/transactions';
import './transfert.css';
const initialState = {
transactions: [],
};
const propTypes = {
transactions: PropTypes.array.isRequired,
};
class Transfert extends Component {
componentDidMount() {
getTransactions();
}
render() {
return (
<div className="table">
</div>
);
}
}
const mapStateToProps = (state) => ({
transactions: state.transactions.transactions,
});
export default connect(mapStateToProps, { getTransactions })(Transfert);
The problem is that I’m not getting the data from the Django server in the reduxdevtools. It displays nothing, only says: type(pin):"##INIT".
Whereas I should be getting all the transactions from there.
Checking the Django server, I found that the Django console says:
code 400, message Bad request version
('\x9bú|$Lª\x01Þù\x83!ów·È\x803<Æ\x00"ZZ\x13\x01\x13\x02\x13\x03À+À/À,À0̨̩À\x13À\x14\x00\x9c\x00\x9d\x00/\x005\x00')
[27/Jan/2022 15:56:59] You're accessing the development server over
HTTPS, but it only supports HTTP.
And I thought of setting the SECURE_SSL_REDIRECT to false in the Django setting. So, I set it there like this:
SECURE_SSL_REDIRECT=False SESSION_COOKIE_SECURE=False CSRF_COOKIE_SECURE=False
But to not avail, I’m still not hitting the right point. All my console logging rendered no result, and I’m even more confused, not knowing whether the problem is in the axios call: axios.get('http://127.0.0.1:8000/api/transaction')
Or from the getTransaction() whitin the React Transaction.js (Rect View). Or is there anything with the
const mapStateToProps = (state) => ({
transactions: state.transactions.transactions,
});
Or with the export default connect(mapStateToProps, { getTransactions })(Transfert);
NOTE: In the transaction.jsx view I earlier called:
componentDidMount() {
this.props.getTransactions();
}
With the this.props but the error says: “Must use destructuring props assignment”
That is why I changed it to current one:
componentDidMount() {
getTransactions();
}
Any help is very much appreciated. Here I’m seriously stuck.
After struggling with this issue throughout this couple of weeks, I found the answer. First of all the “Cors header” mentioned by #Derek S, which I ignored in the beginning was mandatory. But after installing and configuring it, the issue was persisting because of some other misunderstandings which I got to have a good grasp of now.
I was taking example on a tutorial in which several redux folders were created for each of the redux steps: Reducer folder – actions folder– and a store.js file separated. So the main redux action inside my react component was the getTransaction() function declared as:
import axios from 'axios';
import { GET_TRANSACTIONS } from './types';
export const getTransactions = () => (dispatch) => {
axios.get('http://127.0.0.1:8000/api/transaction')
.then((res) => {
dispatch({
type: GET_TRANSACTIONS,
payload: res.data,
});
}).catch((err) => console.log(err));
};
};
The insanity in this is that I was using redux inside a class based
component and the getTransactions() function was not triggered, hence
nothing was dispatched to the redux store.
That was the issue number one. So instead of creation the
getTransaction() inside a separated redux actions folder, I threw it’s
content straight inside the mapDispatchToProps of the target class
componentlike this:
const mapDispatchToProps = () => (dispatch) => {
axios.get('http://127.0.0.1:8000/api/transaction')
.then((res) => {
dispatch({
type: GET_TRANSACTIONS,
payload: res.data,
});
console.log(res.data);
}).catch((err) => console.log(err));
};
And afterward instead of
export default connect(mapStateToProps, { getTransactions })(Transfert);
I rather did
export default connect(mapStateToProps, mapDispatchToProps)(Transfert);
And the redux devtools beautifully displayed the transactions grabbed from API: result is
Another misunderstanding was to be following other people’s
suggestions to configure the cors header for Django, and they were
suggesting to add const cors = require("cors"); and to have:
export const getTransactions = () => (dispatch) => {
axios({
method: 'get',
url: ‘http://127.0.0.1:8000/api/transaction’
withCredentials: false,
content-type: text/plain
params: {
access_token: SECRET_TOKEN,
},
});
};
But this was just another huge pain in the ass, since cors header should only be configured in the backend (the server side) in my case the django API, and not inside the axios' call. So, I got rid of all those suggestions from the react frontend app and kept the component as simple as possible:
import React, {Component,} from 'react';
import { connect } from 'react-redux';
import axios from 'axios';
import { GET_TRANSACTIONS } from '../../../../actions/types';
import PropTypes from 'prop-types';
import './transfert.css';
const initialState = {
transactions: [],
};
const propTypes = {
transactions: PropTypes.array.isRequired,
};
class Transfert extends Component {
render() {
return (
<div className="table">
</div>
);
}
}
const mapStateToProps = (state) => ({
transactions: state.transactions.transactions,
});
const mapDispatchToProps = () => (dispatch) => {
axios.get('http://127.0.0.1:8000/api/transaction')
.then((res) => {
dispatch({
type: GET_TRANSACTIONS,
payload: res.data,
});
console.log(res.data);
}).catch((err) => console.log(err));
};
const connectToStore = connect(mapStateToProps, mapDispatchToProps);
const ConnectedComponent = connectToStore(Transfert);
export default connect(mapStateToProps, mapDispatchToProps)(Transfert);
And now it works fine as the API is able to forward the data:
To me this looks like a problem with your Django server. The error indicates that you are trying to use https to access the api which only supports http, but according to your code you are using http.
Open the debugger in your browser, what does the network traffic look like when the call is made? Is any error returned? Does a prefetch (OPTION) request occur and return OK?
If it is rejected before the actual request is called then there's a configuration issue somewhere. Likely CORS or https instead http.
I am working on a create-react-app generated app and I have an API call to a local JSON file.
Somehow this code generates 8 API calls in a roll.
import '#css/request.scoped.css';
import { useState } from 'react';
import { getAllData } from '#api/api';
function Request() {
let [user, setUser] = useState('');
function changeUser(data) {
setUser(data);
}
getAllData().then((reply) => {
changeUser(reply.data[0].item_name);
});
return <>{user}</>;
}
export default Request;
and here is my Axios instance
import axios from "axios";
const request = axios.create({
baseURL:'./example.json',
timeout: 20000,
});
export const getAllData = () => {
return request({
method: 'get',
url: '',
});
};
Can someone tell me why this happens?
I suspect the most likely cause is that the component is being re-rendered, and currently the logic is to make the API call on every render.
You can set the logic to occur only on the first render of the component with useEffect:
import { useState, useEffect } from 'react';
// then...
useEffect(() => {
getAllData().then((reply) => {
changeUser(reply.data[0].item_name);
});
}, []);
The empty dependency array passed to useEffect means it would open happen once when the component is first loaded, not on subsequent renders. Any dependency you add to that array would cause the operation to be invoked again any time that dependency changes between renders.
I got this code:
import React from 'react'
import fetch from 'isomorphic-unfetch'
import Layout from '../../src/components/layout'
import Nav from '../../src/components/nav'
import Fluid from '../../src/components/fluid'
Post.getInitialProps = async ({ query }) => {
const res = await fetch(`https://schoolvol.mdcholewka.now.sh/api/getPost?slug=${query.slug}`)
const json = await res.json()
return json
}
function Post({ post }) {
return (
<Layout>
<Nav />
<Fluid>
{post ? <p>{post.id}</p> : <p>Ładowanie...</p>}
</Fluid>
</Layout>
)
}
export default Post
The problem is, when I load the site it returns "Ładowanie..." (which means loading in Polish). After a while, nobody happens. What's the solution?
I took a look at this sample: https://nextjs.org/learn/basics/fetching-data-for-pages/fetching-batman-shows. Instead of doing return json, I believe that you have to use return { post: json }.
Nevermind, I just remove these {} out of function's arguments and it's now working fine.
So I finally took the deep dive into hooks. Yes, now I get how easy they can be to use. However, I know one of the most important aspects of it is reusable logic. To share the hook between components, and make my functional component(container now?) even cleaner, how would I separate this? I understand that I can create a custom hook, as long as it starts with use. So for instance, I want to fetch a bunch of tickets and get the length, I have the following:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function TicketCounter() {
const [data, setData] = useState([]);
useEffect(() => {
axios.get(`/ticketapi/ticketslist/`)
.then(res => {
if (res.data) {
setData(res.data)
}
})
}
, []);
return (
<React.Fragment>
{data.length}
</React.Fragment>
);
}
export default TicketCounter
What's the best way to do this? What method are you using? Are you storing these hooks inside your src folder? I imagine you have a folder for hooks, with each hook having it's own js file? Anyhoo, thanks in advance folks. I absolutely LOVE react and all it has to offer, and am super excited about hooks (2 months after everyone else lol).
Well I figured it out.
Sep hook file
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const useFetchAPI = (url) => {
const [data, setData] = useState([]);
useEffect(() => {
axios.get(url)
.then(res => {
if (res.data) {
setData(res.data)
}
})
}
, []);
return data;
};
export default useFetchAPI
and then my component(container?)
import React, { useState, useEffect } from 'react';
import useFetchAPI from '../hooks/TicketCounterHook'
function TicketCounter() {
const url = `/ticketapi/ticketslist/`
const data = useFetch(url)
return (
<React.Fragment>
{data.length}
</React.Fragment>
);
}
export default TicketCounter