Fetch data from API depending on the components props - javascript

I have problems figuring out how to setup the structure where to the fetch data depending on what the props.catergory value is in my PodcastList component and set state
I could fetch the data in my parent component (Home.js), set the state and pass the state as props. But the API endpoint need to take in an categoryId, I cant fetch all podcasts at once.. Thats why I made a child component that takes in and categoryId. Like this:
<PodcastList category='1301' />
And my tought was to do the fetch in the child component passing this.props.category to the api endpoint. (I accutally dont know what im doing)
Can someone help explain how to accomplish what I want?
My code looks like this:
Home.js component:
import React, { Component } from 'react'
import { fetchPopularPodcasts } from './api'
import PodcastList from './PodcastList'
export default class Home extends Component {
render() {
return (
<div className='container'>
<PodcastList category='1301' /> // Lists all Podcasts from category: Arts
<PodcastList category='1303' /> // Lists all Podcasts from category: Comedy
<PodcastList category='1304' /> // Lists all Podcasts from category: Educationrts
<PodcastList category='1305' /> // Lists all Podcasts from category: Kids & Family
</div>
);
}
PodcastList.js component
import React from 'react'
import { fetchPodcastCategory } from './api'
export default class PodcastList extends Component {
state = {
podcasts: [],
loading: true,
}
async componentDidMount () {
const podcasts = await fetchPodcastCategory(this.props.categoryId);
this.setState({
podcasts,
loading: false,
})
}
render() {
return (
<div className='row'>
<div className='col-md-12'>
{category.map((pod) => {
return (
<div className='pod-box'>
{pod.Title}
{pod.Label}
</div>
)
})}
</div>
</div>
)
}
}
export default PodcastList;
Api.js
import Feed from 'feed-to-json-promise'
export async function fetchPopularPodcasts () {
const response = await fetch('https://itunes.apple.com/search?term=podcast&country=se&media=podcast&entity=podcast&limit=200')
const podcasts = await response.json()
return podcasts.results
}
export async function fetchPodcastCategory (categoryId) {
const response = await fetch(`https://itunes.apple.com/se/rss/toppodcasts/limit=100/genre=${categoryId}/explicit=true/json`)
const podcasts = await response.json()
return podcasts.feed
}
export async function fetchPodcast (podId) {
const response = await fetch(`https://itunes.apple.com/lookup?id=${podId}&country=se`)
const podcasts = await response.json()
return podcasts.results
}
export async function fetchPodcastEpisodes (feedUrl) {
const feed = new Feed()
const episodes = await feed.load(feedUrl)
return episodes
}

I would do that inside podcastlist component, if you want data back to parent component you can run a callback,
give a function to podcastlist as a prop and run that function like this,
const podcasts = await fetchPodcastCategory(this.props.categoryId);
this.setState({
podcasts,
loading: false,
},this.props.callback(podcasts))
}

I don't think your design is all too bad.
Basically if you change
{category.map((pod) => {
with
{this.state.podcasts.map((pod) => {
this code should work.
What are you trying to accomplish exactly and why is this architecture not doing it for you? If you clarify this you can get a better answer.

Related

NextJS change data in API from component

I've got a problem with updating data from API in NextJS. I want to increment the value by clicking on the button and then update the API.
My local JSON file is in at http:localhost/api/data
data.js:
export default function handler(req, res) {
res.status(200).json({ value: 0 })
}
I want to update this value from Home component
index.js:
import { useState } from 'react'
export default function Home({data) {
const [currentData, setCurrentData] = useState(data)
return (
<div>
{data.value}
<button onClick={(e) => setCurrentData(currentData.value + 1)}>+</button>
</div>
)
}
export async function getServerSideProps() {
const res = await fetch('http://localhost:3000/api/data')
const data = await res.json()
return {
props: { data },
}
}
Is there any way to do it? If something is unclear feel free to ask :)

How to manage with data from fetch promise?

I am beginner in JS and React.
I have a problem:
import React from "react";
import JsonApi from "../../services/jsonApi";
const UserPage = () => {
const jsonApi = new JsonApi(); //it is my class which has methods
//to manage with data(get,post,etc);
const user = jsonApi.getUser(); //returns promise,but i need an object with data!
//promise has such view:
//[[Prototype]]: Promise
//[[PromiseState]]: "fulfilled"
//[[PromiseResult]]: Object !!!!i need this data!!!!
console.log(user); //Promise.
/* i know that a i can do so:
user.then((data) => console.log(data));
but,using this way,i can only log!But i need an object with data!
*/
return (
<div className="app">
<h1>{user.name}</h1>
<p>Here are info about users!</p>
</div>
);
};
export default UserPage;
I understand that i need to use await before const user = jsonApi.getUser();
but we can do that only inside async functions.
So,i tried to do that: const UserPage = async () => { }
but i had a mistake:
In order to perform side effects in react you should consider using useEffect hook. After the effect you need to store the data retrieved in react state by using the useState hook. In the end your code would look like below:
import React, { useState, useEffect } from "react";
import JsonApi from "../../services/jsonApi";
const UserPage = () => {
const [user, setUser] = useState(null);
useEffect(() => {
const jsonApi = new JsonApi();
jsonApi.getUser().then((user) => {
setUser(user);
});
}, []);
if (!user) return null;
return (
<div className="app">
<h1>{user.name}</h1>
<p>Here are info about users!</p>
</div>
);
};
export default UserPage;
Keep in mind that the user is not populated until you async getUser resolves, so you have to handle the case where user data are not yet present, either by rendering nothing (null) or by showing some loading state in between.

Why is my fetch promise being stored in the prop as empty instead of the json i'm fetching?

I'm trying to grab json from my backend to fill a table on the front end. Nothing is loading and in the react debugging tools it says the table prop is empty.
I've added async to the function that is doing the fetching, but it still seems to pass the json to the prop before its finished (not totally sure).
EDIT: lines are missing in the code because I cut out what was irrelevent
in app.js
import React, { Component } from 'react'
import Table from './Table'
class App extends Component {
render() {
const repos = getGitHubRepos()
return (
<div className="container">
<Table repoData={repos} />
</div>
)
}
}
async function getGitHubRepos() {
const response = await fetch('valid url i'm hiding')
return await response.json()
}
export default App
in table.js
import React, { Component } from 'react'
class Table extends Component {
render() {
const { repoData } = this.props
return (
<table>
<TableHeader />
<TableBody repoData={repoData} />
</table>
)
}
}
const TableBody = props => {
const rows = props.repoData.map((row, index) => {
return (
<tr key={index}>
<td>{row.name}</td>
<td>{row.lang}</td>
</tr>
)
})
return <tbody>{rows}</tbody>
}
export default Table
I expect the output to map each bit of json into the table but it isn't doing that because the prop is empty when it gets to table.js
You can perform async tasks in an async componentDidMount method:
class App extends Component {
constructor() {
super();
this.state = { repos: [] };
}
async componentDidMount() {
const repos = await getGitHubRepos();
this.setState({ repos });
}
render() {
return (
<div className="container">
<Table repoData={this.state.repos} />
</div>
);
}
}
async function getGitHubRepos() {
const response = await fetch("valid url Im hiding");
return response.json();
}
export default App;
Be adviced async/await syntax is not covered by all browsers
Move your fetching logic in one of Reacts life-cycle methods I would suggest componentDidMount, you should never fetch anything in the render method, you should even avoid extensive calculations there.. after you get the data I would save it in the local component state with this.setState({someState: data}) .. when the state is changed your component will automatically re render. You can read you data from you component state this this.state.someState
You get a thenable promise from fetch you can use .then(function () {}) to define what happens when the data is fetched
function getGitHubRepos() {
fetch('valid url i'm hiding')
.then(function (res) {
this.setState({data: res.data}); // or something similar
});
}

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.

Server side load a global component with Next.js in React

The fact that I can't find anyone asking this question probably means that I'm not understanding something fully or I'm searching with the wrong keywords so please don't bite my head off if this is a stupid question.
I'm pasting the relevant parts of the code but if you want a repo of the full example, here it is. The full question will be at the bottom.
Here's my folder structure:
server.js
/components
Layout.js
/pages
contact.js
server.js
// tells next which page to load
server.get("/contact/:id", (req, res) => {
const actualPage = "/contact"
const queryParams = {id: req.params.id}
app.render(req, res, actualPage, queryParams)
})
// api uri for grabbing contacts from the database
server.get("/api/contact/:id", (req, res) => {
Contact.findOne({first_name: req.params.id}, (error, contact) => {
if (error) return next(error)
res.status(200).json(contact)
})
})
pages/contact.js
const Contact = props => (
<Layout>
<h1>{props.contact.first_name} {props.contact.last_name}</h1>
</Layout>
)
// a static async call passes fetched data into the props
Contact.getInitialProps = async function (context) {
const {id} = context.query
const res = await fetch(`http://localhost:3000/api/contact/${id}`)
const contact = await res.json()
return {contact: contact}
}
components/Layout.js
const Layout = (props) =>
<div>
<div>
<Link href="/contact/John">
<a>John</a>
</Link>
<Link href="/contact/Jed">
<a>Jed</a>
</Link>
<Link href="/contact/Fred">
<a>Fred</a>
</Link>
</div>
{props.children}
</div>
I'm trying to figure out whether or not it's possible to dynamically query the database to build a navigation of the documents in the database. The only way I can think to do it is by re-rendering the entire navigation with every component but this seems extremely unnecessary. Again, if you want to give the code a try, here's my example repo.
One of the ways I can think of is to use custom app.js and add componentDidMount method (it is fired only once) where you can fetch all the contacts, store it inside app.js state and pass it down to pages and components.
_app.js
import React from 'react';
import App, { Container } from 'next/app';
export default class MyApp extends App {
static async getInitialProps({ Component, router, ctx }) {
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
return { pageProps };
}
// store contacts in the state
state = {
contacts: undefined
};
componentDidMount() {
// get contacts and store them in the _app.js state
fetch('some-api/all-contacts-endpoint').then(contacts => {
this.setState({ contacts });
});
}
render() {
const { Component, pageProps } = this.props;
return (
<Container>
<Component {...pageProps} contacts={this.state.contacts} />
</Container>
);
}
}
pages/contact.js
// contacts will be inside props here
const Contact = props => (
<Layout contacts={props.contacts}>
<h1>
{props.contact.first_name} {props.contact.last_name}
</h1>
</Layout>
);
// a static async call passes fetched data into the props
Contact.getInitialProps = async function(context) {
const { id } = context.query;
const res = await fetch(`http://localhost:3000/api/contact/${id}`);
const contact = await res.json();
return { contact: contact };
};
components/Layout.js
const Layout = ({ contacts = [] }) => (
<div>
<div>
{contacts.map(contact => (
<Link key={contact.id} href={`/contact/${contact.id}`}>
<a>{contact.name}</a>
</Link>
))}
</div>
{props.children}
</div>
);
Hope this helps!

Categories