How to display a map function from a fetch REACT.js - javascript

This is my first time using this website so bear with me...
I am trying to display an array of 10 youtube videos from a playlist using the youtube api. So far I am able to console.log the json data from the api. Although the issue arrives when I attempt to display that json data in my react app using inside a map function.
Here is all my code :
import React, { Component } from "react";
import './Videos.css';
import VideosCP from '../Pages/VideosPage/VideosSearch'
import youtubeSearch from "../../api/youtubeSearchApi";
import VideosList from "../Pages/VideosPage/VideoList";
const YOUTUBE_PLAYLIST_ITEMS_API = 'https://www.googleapis.com/youtube/v3/playlistItems';
let url = `${YOUTUBE_PLAYLIST_ITEMS_API}?part=snippet&playlistId=myPlaylistId&maxResults=10&key=myYoutubekey`
function Demo() {
}
class Videos extends Component{
state = {
videoMetaInfo:[],
selectedVideoId: null
}
onVideoSelected = videoId => {
this.setState({
selectedVideoId:videoId
})
}
onSearch = async keyword => {
const response = await youtubeSearch.get("/search",{
params:{
q:keyword
}
})
console.log(this.state);
this.setState({
videoMetaInfo: response.data.items,
selectedVideoId: 'Nl3DoewG3Co'
})
}
playlistRender = async () => {
const data = await fetch(url);
let commits = await data.json();
console.log('data', commits)
}
render(){
const Data = ({data}) => {
data = this.playlistRender()
const results = data.items.map((item)=>{
const {id, snippet = {}} = item
const title = snippet
return(
<li key={id}>
<h3>{title}</h3>
</li>
)
});
return results
}
return(
<div className="wrapper">
<div className="wrapper-inner scroll">
<VideosCP onSearch={this.onSearch}/>
<VideosList onVideoSelected={this.onVideoSelected}
data={this.state.videoMetaInfo}/>
<ul>
<Data/>
</ul>
</div>
</div>
);
}
}
export default Videos
The code compiles fine in the terminal but once inside the browser it spits the error : "Cannot read property 'map' of undefined".
Hence, the error is caused by this line which states : data.items.map((item)=>{
What's even more weirder is that the console in the browser recognizes that there is an "items" in the array. Why is the browser spitting this error at me? Can it not map the items? If so why not? And how is this error fixable?
I have tried everything so far and this is the closest I've gotten but yet no results.
Please help me, stack overflow is my only hope...

The data you're loading from the API is stateful, so you'll want to set it to state once it's loaded. Then, you can map over this stateful information once it exists.
I also added a componentDidMount method call to make sure we call the API once the component mounts.
class Videos extends Component{
state = {
videoMetaInfo:[],
selectedVideoId: null,
commits: []
}
onVideoSelected = videoId => {
this.setState({
selectedVideoId:videoId
})
}
onSearch = async keyword => {
const response = await youtubeSearch.get("/search",{
params:{
q:keyword
}
})
console.log(this.state);
this.setState({
videoMetaInfo: response.data.items,
selectedVideoId: 'Nl3DoewG3Co'
})
}
playlistRender = async () => {
const data = await fetch(url);
let commits = await data.json();
this.setState({ commits });
}
componentDidMount() {
this.playlistRender();
}
render(){
return(
<div className="wrapper">
<div className="wrapper-inner scroll">
<VideosCP onSearch={this.onSearch}/>
<VideosList onVideoSelected={this.onVideoSelected}
data={this.state.videoMetaInfo}/>
<ul>
{this.state.commits.map((item) => {
const {id, snippet = {}} = item
const title = snippet
return (
<li key={id}>
<h3>{title}</h3>
</li>
)
})}
</ul>
</div>
</div>
);
}
}
export default Videos

Related

map function not working in ReactJs using nexttjs

I am new in ReactJS and i am using "Nextjs" framework,Right now i am using "async" function
for fetching data but unable to fetch using "map" function, in console.log ...showing me following message
" items: undefined }",Here is my code,Where i am wrong ?
import React, { Component } from 'react';
const blog =({items}) =>{
console.log({items});
return(
<div>
</div>
);
};
//calling api for get data
export const getstaticprops=async()=>{
console.log('Om Success');
const res=await fetch('https://jsonplaceholder.typicode.com/posts');
const posts = await res.json()
return {
props: { items: posts },
}
}
export default blog
test this code !
const blog =({posts}) =>{
console.log(posts); \\ fix console log
return(
<div>
</div>
);
};
export async function getServerSideProps(){
console.log('Om Success');
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
const posts = await res.json()
return {
props: { posts }, \\ remove items
}
}

Await result of API call and update list component

I am calling a REST API and return chat rooms. Those get returned fine, as I can see in the payload of the object via the console.
Now I want to display this in a list.
I was able to display a list and download the chat rooms, but not combine both. This is what I did:
import * as React from 'react';
export default function ChatRoomList({param1}) {
var x = getChatRoom(param1)
console.log(x)
return (
<div>
<li>{param1}</li>
<li> asd </li>
<li> asd </li>
</div>
);
}
async function getChatRoom(status) {
// status could be either 'open' or 'room'
var dict = {
chatType: status,
};
var adminChats = await callKumulosAPIFunction(dict, 'getAdminChat')
return adminChats
}
Now I did try to simply await the first getChatRoom(param1) call. But I can only await inside an async function. I then tried to add the async keyword to the export function, but this would crash the whole app.
How would the workflow be here? And how would I map the result from getChatRoom onto a listview?
The result of the adminChats (console.log(adminChats)):
You need to use useEffect hook to get remote data:
export default function ChatRoomList({param1}) {
React.useEffect(()=>{
(async () => {
var x = await getChatRoom(param1)
console.log(x)
})()
},[])
return (
<div>
<li>{param1}</li>
<li> asd </li>
<li> asd </li>
</div>
);
}
If the data returned from getChatRoom is an array and you want to show it, you need to save the response in a state, or the component will not re-render:
export default function ChatRoomList({param1}) {
const [chatRooms, setChatRooms] = React.useState([]);
React.useEffect(()=>{
(async () => {
var x = await getChatRoom(param1)
setChatRooms(x)
})()
},[])
return (
<div>
<li>{param1}</li>
{chatRooms.map((chatRoom , index) => {
return <li key={index}>{JSON.stringify(chatRoom)}</li>
})}
</div>
);
}
async function getChatRoom(status) {
// status could be either 'open' or 'room'
var dict = {
chatType: status,
};
var adminChats = await callKumulosAPIFunction(dict, 'getAdminChat')
return adminChats.payload
}
I suggest you to read the documentation about React hooks and lifecycle management:
https://reactjs.org/docs/hooks-intro.html
https://reactjs.org/docs/state-and-lifecycle.html
You need to use useEffect hook to call the API.
Read more about hooks
import * as React from 'react';
function getChatRoom(status) {
const [chats, setChats] = React.useState([]);
const getChart = async (status) => {
// status could be either 'open' or 'room'
const dict = {
chatType: status,
};
const adminChats = await callKumulosAPIFunction(dict, 'getAdminChat');
setChats(adminChats);
};
React.useEffect(() => {
getChart(status)
}, [status])
return { chats };
};
export default function ChatRoomList({param1}) {
const { chats } = getChatRoom(param1)
console.log(chats)
return (
<div>
<li>{param1}</li>
{chats?.map((chatRoom , index) => {
return <li key={index}>{JSON.stringify(chatRoom)}</li>
})}
</div>
);
}
Dependencies argument of useEffect is useEffect(callback, dependencies)
Let's explore side effects and runs:
Not provided: the side-effect runs after every rendering.
import { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// Runs after EVERY rendering
});
}
An empty array []: the side-effect runs once after the initial rendering.
import { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// Runs ONCE after initial rendering
}, []);
}
Has props or state values [prop1, prop2, ..., state1, state2]: the side-effect runs only when any dependency value changes.
import { useEffect, useState } from 'react';
function MyComponent({ prop }) {
const [state, setState] = useState('');
useEffect(() => {
// Runs ONCE after initial rendering
// and after every rendering ONLY IF `prop` or `state` changes
}, [prop, state]);
}

Objects are not valid as react child, but I'm using React docs official code [duplicate]

I am trying to render a list of posts by mapping through an array. I've done this many times before but for some reason
renderPosts = async () => {
try {
let res = await axios.get('/posts');
let posts = res.data;
return posts.map((post, i) => {
return (
<li key={i} className="list-group-item">{post.text}</li>
);
});
} catch (err) {
console.log(err);
}
}
render () {
return (
<div>
<ul className="list-group list-group-flush">
{this.renderPosts()}
</ul>
</div>
);
}
All I get is:
Uncaught 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.
I've checked the data returned from renderPosts and it is an array with the correct values and no promises. What's going on here?
I also received the same error message when creating an async functional component. Functional components should not be async.
const HelloApp = async (props) => { //<<== removing async here fixed the issue
return (
<div>
<h2>Hello World</h2>
</div>
)
}
ReactDOM.render(<HelloApp />, document.querySelector("#app"))
jsfiddle
this.renderPosts() will return a Promise not the actual data, and AFAIK Reactjs will not resolve Promises implicitly in render.
You need to do it like this
componentDidMount() {
this.renderPosts();
}
renderPosts = async() => {
try {
const res = await axios.get('/posts');
const posts = res.data;
// this will re render the view with new data
this.setState({
Posts: posts
});
} catch (err) {
console.log(err);
}
}
render() {
const posts = this.state.Posts?.map((post, i) => (
<li key={i} className="list-group-item">{post.text}</li>
));
return (
<div>
<ul className="list-group list-group-flush">
{posts}
</ul>
</div>
);
}
Using React Hooks:
UPDATE 2020-08-01: Amended with #ajrussellaudio's suggestion.
import React, {useState, useEffect} from "react"
const ShowPosts = () => {
const [posts, setPosts] = useState([]);
useEffect( () => {
async function fetchData() {
try {
const res = await axios.get('/posts');
setPosts(res.data);
} catch (err) {
console.log(err);
}
}
fetchData();
}, []);
return <div>{posts}</div>
}
Poor me
For anyone using jest test
And trying to call a function children then received this Error
please check:
const children = jest.fn().mockReturnValueOnce(null)
NOT
const children = jest.fn().mockRejectedValue(null);
If you use Next.js you can use this:
Put your codes in {}, if you have some connection or some calculations thats need time, add await to your codes in {}.
import { useEffect } from 'react'
useEffect(async () => {.......},[])

How to set the default value to props

The below code fetch the URL data and retrieve All the film list with the duration in seconds
But I want to list out only the films which have duration more than 5000 secs
*The component should have a prop minDuration and only films with a duration more than this value should be listed; the default value for this prop should be 5000
Could you please clarify how to solve this
import React, { useState, useEffect } from "react";
export default function App() {
const [films, setFilms] = useState([]);
useEffect(() => {
const listOfFilms = async () => {
const response = await fetch(
"https://api.flixpremiere.com/v1/films/filter/now_showing?limit=10"
);
console.log("here", response.data);
const jsonresponse = await response.json();
console.log("here11", jsonresponse.films);
setFilms(jsonresponse.films);
};
listOfFilms();
}, []);
return (
<div className="App">
<h1>Film Title</h1>
{console.log("films", films.slug, films.films)}
{films.map((film, index) => (
<ul>
<li key={film.title}>
{film.title} {`(${film.duration_seconds})`} </li>
</ul>
))}
</div>
);
}
I'm going to answer the question as it's written, which is "How to set the default value to props".
const Component = ({ property = 5000 }) => { ... }
If you want to only show certain items based on criteria, use the filter function.
The only one way is use default parameter in your function component.
const Component = ({ iNeedDefaultValue = "foo" }) => { ... }
The rest of is deprecated (words of "father of react").
Because defaultProps on functions will eventually get deprecated.
See: https://twitter.com/dan_abramov/status/1133878326358171650
for setting default props for a component, you can use of defaultProps property
class Tooltip extends React.Component {
// ...
}
Tooltip.defaultProps = {
delay: 100,
}

Consuming Paginated API in React Component

I'm just getting started with React. As a simple exercise, I wanted to create some components for viewing data retrieved from the JsonMonk API. The API contains 83 user records and serves them in pages of 10.
I am trying to develop a component for viewing a list of users one page at a time which I called UserList. The code for it is below:
class UserList extends React.Component {
constructor(props) {
super(props);
this.state = {
pageNumber: 1,
users: [],
};
this.onPageNext = this.onPageNext.bind(this);
}
componentDidMount() {
this.fetchUsers(this.state.pageNumber)
.then((users) => this.setState({users: users}));
}
async fetchUsers(pageNumber) {
const response = await fetch(`https://jsonmonk.com/api/v1/users?page=${pageNumber}`);
const jsonResponse = await response.json();
return jsonResponse.data.records;
}
onPageNext() {
// ...
}
render() {
const postElements = this.state.users.map(
(props) => <User key={props._id} {...props} />);
return (
<div>
{postElements}
<div>
<button onClick={this.onPageNext}>Next</button>
</div>
</div>
);
}
}
The problem I am having pertains to the onPageNext method of my component. When the user clicks the "Next" button, I want to make a fetch for the next page of data and update the list.
My first attempt used an asynchronous arrow function passed to setState like so:
onPageNext() {
this.setState(async (state, props) => {
const nextPageNumber = state.pageNumber + 1;
const users = await this.fetchUsers(nextPageNumber);
return {pageNumber: nextPageNumber, users: users}
})
}
However, it does not seem React supports this behavior because the state is never updated.
Next, I tried to use promise .then syntax like so:
onPageNext() {
const nextPageNumber = this.state.pageNumber + 1;
this.fetchUsers(nextPageNumber)
.then((users) => this.setState({pageNumber: nextPageNumber, users: users}));
}
This works but the problem here is that I am accessing the class's state directly and not through setState's argument so I may receive an incorrect value. Say the user clicks the "Next" button three times quickly, they may not advance three pages.
I have essentially run into a chicken-or-the-egg type problem. I need to pass a callback to setState but I need to know the next page ID to fetch the data which requires calling setState. After studying the docs, I feel like the solution is moving the fetch logic out of the UsersList component, but I'm not entirely sure how to attack it.
As always, any help is appreciated.
You need to change onPageNext as below:
onPageNext() {
this.setState( prevState => {
return {pageNumber: prevState.pageNumber + 1}
}, () =>{
this.fetchUsers(this.state.pageNumber).then(users => this.setState({users: users}) )
});
}
Here is the Complete Code:
import React from "react";
export default class UserList extends React.Component {
constructor(props) {
super(props);
this.state = {
pageNumber: 1,
users: [],
};
this.onPageNext = this.onPageNext.bind(this);
}
componentDidMount() {
this.fetchUsers(this.state.pageNumber)
.then((users) => {
console.log(users, 'users');
this.setState({users: users})
}
);
}
async fetchUsers(pageNumber) {
const response = await fetch(`https://jsonmonk.com/api/v1/users?page=${pageNumber}`);
const jsonResponse = await response.json();
return jsonResponse.data.records;
}
onPageNext() {
this.setState( prevState => {
return {pageNumber: prevState.pageNumber + 1}
}, () =>{
this.fetchUsers(this.state.pageNumber).then(users => this.setState({users: users}) )
});
}
render() {
const postElements = this.state.users.map(
(user) => <User key={user._id} {...user} />);
return (
<div>
{postElements}
<div>
<button onClick={this.onPageNext}>Next</button>
</div>
</div>
);
}
}
function User(props) {
return (
<div>
<div style={{padding: 5}}>Name: {props.first_name} {props.last_name}</div>
<div style={{padding: 5}}>Email: {props.email}</div>
<div style={{padding: 5}}>Phone: {props.mobile_no}</div>
<hr/>
</div>
);
}
Here is the Code Sandbox

Categories

Resources