React problem with getting image from API - javascript

I am currently practicing React, and my goal is to build a simple app that searches movies and display some short info about them as results. I managed to pull data from API and store em in React hooks. I can access any data, but when I try to pull images I get error:
TypeError: Cannot read property 'medium' of null.
Here are the API results:
http://api.tvmaze.com/search/shows?q=$girls
I find an image that I want to use stored in {show.image.medium}
Here is my React code:
import React, {useState, useEffect} from 'react';
import Movie from './Movie';
const App = () => {
const [movies, setMovies] = useState([]);
useEffect(() => {
getMovies();
}, []);
const getMovies = async () => {
const response = await fetch(`http://api.tvmaze.com/search/shows?q=$girls`);
const data = await response.json();
setMovies(data);
console.log(data)
;}
return (
<div>
<form className='search-form'>
<input type='text' className='search-bar' placeholder='search movie'>
</input>
<button type='submit' className='search-button'>
Search
</button>
</form>
{movies.map(movie => (
<Movie title={movie.show.name} image={movie.show.image.medium} />
))}
</div>
);
};
export default App;
and Movie.js file:
import React from 'react';
const Movie = ({title, image}) => {
return(
<div>
<h1>{title}</h1>
<img src={image} alt=''/>
</div>
);
}
export default Movie;
so I basically mapped the results in movie array, but {movie.show.image.medium} just won't work, while pulling any other data work just fine.
I know that this is probably an easy fix, but I tried everything and searched for an answer for hours and still, nothing worked. I would really appreciate it if someone can explain to me what I am doing wrong. Thanks in advance!

In the API call there is one value where movie.show.image is technically null. For null you could not get any properties, even medium.
What you can do as a solution is the following:
{
movies.map(movie =>
movie.show.image ?
<Movie title={movie.show.name} image={movie.show.image.medium} /> :
null)
}
Additionally you need to return from Array.prototype.map().
Iteration from the API on my console:
I hope that helps!

Related

Axios and getRequest from external component

I'm new to React.js and I'm actually trying to create a page structured in the following way:
-A form to insert [version,date,image,content] and post a JSON via POST request (with Axios)
-A display part in which I GET the same data and display on screen by clicking on a button
actually I'm able to do this by introducing the Get and Post logic in the used component.
In order to use Components and have a clear code, i would to have a separate component to call inside the various components to make a GET or POST request.
By using hooks I'm not able to do this. The actual code is:
This is UserGet.js
import axios from "axios";
import {useState} from "react";
const baseURL = "http://localhost:8088/";
const userURL ="changelog/version.txt";
function UserGet(props) {
const [get, setGet] = useState(null);
axios.get(baseURL+userURL).then((response) => {
setGet(response.data);
});
return [get, setGet] ;
}
export default UserGet;
while in the component I want to use as data displayer:
const DisplayInfo =(props) => {
const [items, setItems] = useState([]);
const onFinish = (() => {
setItems(UserGet());
})
const DisplayData = items.map(
(info)=>{
return(
<div className='changelogDisplay' key={info.version}>
<button className='version' >v {info.version} </button>
<div className='datelog' > {info.data} </div>
<div className='dataHeader' > {info.title} </div>
<div className='whatsnew' > {info.text} </div>
<div className='imageLog' alt='Changelog pic' >{info.img} </div>
</div>
)
});
return(
<div>
<Form onFinish={onFinish}>
<Form.Item>
<Button type="primary" htmlType="submit" name='Refresh'> Refresh list</Button>
<div className='displayData'>
{DisplayData}
</div>
</Form.Item>
</Form>
</div>
)
}
export default DisplayInfo;
Actually, if I use
const response = UserGet();
I'm able to get data and insert into items for further process. I want to get the data using Onfinish or Onclick properties but I'm not able due to Hooks that cannot stay inside a function. How can I get a solution?
I tried different ways, and I don't want to use Redux at the moment in order to improve my knowledge on this.
Hooks are not so simple to me
Currently you're setting the items state to the [get, setGet] value that returns from the UserGet hook.
Here you return the data back from the request.
async function UserGet(props) {
const response = await axios.get(baseURL + userURL);
return response.data;
}
Then in the onFinish
const onFinish = (() => {
const newItems = UserGet();
setItems(newItems);
// or
// setItems(UserGet());
});
I hope this helps you with your project!

The data fetched from an API doesn't populate the table in NextJS

I try to populate a table with an API call in NextJS. I use getStaticProps function to get the data and pass them to the component. I tried map() function to access each data in the object but it doesn't work and doesn't populate the required data. when I run the app the console outputs the below error.
Warning: data for page "/" is 150 kB, this amount of data can reduce performance.
When I checked the page source it has the full API object and I don't understand why it does that. Below I have put the code of index.js and CountriesTable.js
index.js
import Head from "next/head";
import CountriesTable from "../components/CountriesTable/CountriesTable";
import Layout from "../components/Layouts/layout";
import SearchInput from "../components/SearchInput/SearchInput";
import styles from "../styles/Home.module.css";
export default function HomePage({ countries }) {
return (
<Layout>
<div className={styles.counts}>found {countries.length} countries</div>
<SearchInput placeholder="Search for country" />
<CountriesTable countries={countries} />
</Layout>
);
}
export const getStaticProps = async () => {
const res = await fetch("https://restcountries.com/v3.1/region/asia");
const countries = await res.json();
return {
props: {
countries,
},
};
};
CountriesTable.js
import styles from './CountriesTable.module.css';
export default function CountriesTable({countries}) {
return (
<div>
<div className={styles.heading}>
<button className={styles.heading_name}>
<div>Name</div>
</button>
<button className={styles.heading_population}>
<div>Population</div>
</button>
</div>
{countries.map((country) => {
<div className={styles.row}>
<div className={styles.name}>{country.name}</div>
<div className={styles.population}>{country.population}</div>
</div>
})}
</div>
);
};
How can I resolve the error in the console and populate the table with data? TIA!
NextJS is a server rendered platform. Any props you pass to pages/components will be serialized in the source itself. Therefore, it is very important to trim data to fields which are necessary. Otherwise you will see entire json in the HTML source. (Working codesandbox)
Reduce the amount of data returned from getStaticProps, getServerSideProps, or getInitialProps to only the essential data to render the page.
As only name & population are required we can create a new dataset with these two properties only. This should get rid of the warning as well.
export const getStaticProps = async () => {
const res = await fetch("https://restcountries.com/v3.1/region/asia");
const countries = await res.json();
const countriesWithNamePopulation = countries.map((country) => ({
name: country.name.common,
population: country.population
}));
return {
props: {
countries: countriesWithNamePopulation
}
};
};
countries.map needs a return statement
{countries.map((country) => {
return (
<div>
<div>{country.name}</div>
<div>{country.population}</div>
</div>
);
})}
I cannot reproduce your console warning but you are missing the return statement inside your map function.
This works for me:
{countries.map((country, index) => {
return(
<div key={index}>
<div>{country.name.common}</div>
<div>{country.population}</div>
</div>
)
})}
Furthermore you need cannot display country.name as it is an object. I used country.name.common which is valid as it is a string. Additionally you need to add a key property to your div inside the map function.

react fetching and mapping data

import React,{useState, useEffect} from 'react'
import { useParams } from 'react-router-dom'
import Home from './Home'
import './detailpage.css'
function DetailPage({name,
info,
genre,
_id,
episodeNumber,
poster}) {
const [shows, setShows]= useState([{name:'',
info:'',
airingDate:'',
_id:'',
genre:'',
episodeNumber:'',
poster:''
}])
const params= useParams();
useEffect(()=>{
fetch("/home")
.then(res => res.json())
.then(jsonRes => setShows(jsonRes))
}, [])
const b = JSON.stringify(params);
const newShows = shows.filter(a=>a._id===b)
console.log(newShows)
return (
<div>
<h2>.</h2>
<h2>.</h2>
<h2>.</h2>
<h2>{JSON.stringify(params)}</h2>
<h2>{shows.genre}</h2>
{newShows.map(a=>
<div>
<div className='container'>
<img className='showImg' src={a.poster} alt=''></img>
<h2 className='showTitle'>{a.title}</h2>
<h3>{a.genre}</h3>
<p className='showInfo'>{a.info} </p>
</div>
</div>
)}
<h2>{episodeNumber}</h2>
<h2>{shows.info}</h2>
</div>
)
}
export default DetailPage
I have tv shows on my Home page and after clicking the image I want it to load the detail page about the clicked show however I couldn't manage to do it. I tried 'filter' method in the code but it didn't work I also tried like this
const newShows = shows.filter(a=>a.genre.length>5)
it works but this is not what I want. I would be really happy if someone could've helped. Thank you so much.
If I were you, I wouldn't use this fetch, as when you click on the image from your home you already know which tv show you want to display more details about.
I would use something like useLocation from react-router-dom, and while changing pages (home -> detail page about a tv show) carry a state variable with the specific tv show details.
https://v5.reactrouter.com/web/api/Hooks/usehistory
const handleClick = (state) => {
history.push({ pathname: "/detail-page", state })
}
<YourTvShowImage onClick={() => handleClick(TvShowData)} />
Then on your detail page class you use something like
https://v5.reactrouter.com/web/api/Hooks/uselocation
const location = useLocation()
const [tvShowData, setTvShowData] = useState()
useEffect(() => {
if (location.state) {
setTvShowData(location.state)
}
}, [location])

Sanity Error: `dataset` must be provided to perform queries with React

I'm trying to create a blog with Sanity CMS; when I try to get data from Sanity database i get that
"Error: dataset must be provided to perform queries"
I got client.js component with projectID and database name
the component that gives me error looks like this:
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import sanityClient from "../client.js";
export default function AllPosts() {
const [allPostsData, setAllPosts] = useState(null);
useEffect(() => {
sanityClient
.fetch(
`*[_type == "post"]{
title,
slug,
mainImage{
asset->{
_id,
url
}
}
}`
)
.then((data) => setAllPosts(data))
.catch(console.error);
}, []);
return (
<div>
<h2>Blog Posts</h2>
<h3>Welcome to my blog posts page!</h3>
<div>
{allPostsData &&
allPostsData.map((post, index) => (
<Link to={"/" + post.slug.current} key={post.slug.current}>
<span key={index}>
<img src={post.mainImage.asset.url} alt="" />
<span>
<h2>{post.title}</h2>
</span>
</span>
</Link>
))}
</div>
</div>
);
}
Used the same tutorial, and encountered the same error. Under "client.js", make sure that database is set to "production".
In my case the mistake was in the API version property inside Client.js
I cchanged it to 2022-02-01 and it worked perfectly fine for me. Don't know exactlywas is the reason for this, but still, it got the problem solved.
For me, the issue was I spelled dataset: "dataSet".
No Camel case here folks, use: "dataset: 'production',

TypeError: Cannot read property 'map' of undefined when I am trying to access API call data

I am trying to make a movie search app with React and have made an API call to The Movie Database API. I have this form and what I am trying to do is get the data of the movie that I am searching for.
I am not able to access the data from the API call, and I get this error of "Uncaught TypeError: Cannot read property 'map' of undefined"
I have two js files:
1 index.js
import React from 'react';
import ReactDOM from 'react-dom';
import SearchMovies from "./searchMovies";
import './style.css';
class Main extends React.Component {
render() {
return (
<div className="container">
<h1 className="title">React Movie Search</h1>
<SearchMovies/>
</div>
);
}
}
ReactDOM.render(<Main />, document.getElementById('root'));
The second file is searchMovies.js
import React, {useState} from "react";
export default function SearchMovies(){
//states- input query, movies
const [query, setQuery] = useState('');
const [movies, setMovies] = useState([]);
const searchMovies = async (e) => {
e.preventDefault();
const url = `https://api.themoviedb.org/3/movie/550?
api_key=api_key&language=en-US&query=${query}&page=1&
include_adult=false`;
try {
const res = await fetch(url);
const data = await res.json();
setMovies(data.results);
}catch(err){
console.error(err);
}
}
return(
<div>
<form className="form" onSubmit={searchMovies}>
<label htmlFor="query" className="Label">Movie Name</label>
<input className="input" type="text" name="query" placeholder="i.e. Jurassic
Park"
value={query} onChange={(e) => setQuery(e.target.value)}
/>
<button className="button" type="submit">Search</button>
</form>
<div className="card-list">
{movies.map(movie => (
<div className="card">
<img className="card--image"
src={`https://image.tmdb.org/t/p/w185_and_h278_bestv2/
${movie.poster_path}`}
alt={movie.title + ' poster'}
/>
</div>
))}
</div>
</div>
)
}
Can somebody tell me what I am doing wrong here? I am new to React.
Many thanks!
Your API response an object, not an array that's why the map function not working.
See Your API response:
{"adult":false,"backdrop_path":"/rr7E0NoGKxvbkb89eR1GwfoYjpA.jpg","belongs_to_collection":null,"budget":63000000,"genres":[{"id":18,"name":"Drama"}],"homepage":"http://www.foxmovies.com/movies/fight-club","id":550,"imdb_id":"tt0137523","original_language":"en","original_title":"Fight Club","overview":"A ticking-time-bomb insomniac and a slippery soap salesman channel primal male aggression into a shocking new form of therapy. Their concept catches on, with underground "fight clubs" forming in every town, until an eccentric gets in the way and ignites an out-of-control spiral toward oblivion.","popularity":46.801,"poster_path":"/pB8BM7pdSp6B6Ih7QZ4DrQ3PmJK.jpg","production_companies":[{"id":508,"logo_path":"/7PzJdsLGlR7oW4J0J5Xcd0pHGRg.png","name":"Regency Enterprises","origin_country":"US"},{"id":711,"logo_path":"/tEiIH5QesdheJmDAqQwvtN60727.png","name":"Fox 2000 Pictures","origin_country":"US"},{"id":20555,"logo_path":"/hD8yEGUBlHOcfHYbujp71vD8gZp.png","name":"Taurus Film","origin_country":"DE"},{"id":54051,"logo_path":null,"name":"Atman Entertainment","origin_country":""},{"id":54052,"logo_path":null,"name":"Knickerbocker Films","origin_country":"US"},{"id":25,"logo_path":"/qZCc1lty5FzX30aOCVRBLzaVmcp.png","name":"20th Century Fox","origin_country":"US"},{"id":4700,"logo_path":"/A32wmjrs9Psf4zw0uaixF0GXfxq.png","name":"The Linson Company","origin_country":""}],"production_countries":[{"iso_3166_1":"DE","name":"Germany"},{"iso_3166_1":"US","name":"United States of America"}],"release_date":"1999-10-15","revenue":100853753,"runtime":139,"spoken_languages":[{"iso_639_1":"en","name":"English"}],"status":"Released","tagline":"Mischief. Mayhem. Soap.","title":"Fight Club","video":false,"vote_average":8.4,"vote_count":20153}
I made a jsfiddle with your code for testing: https://jsfiddle.net/hqm6rcpf/
const url = `https://api.themoviedb.org/3/movie/550?
api_key=api_key&language=en-US&query=${query}&page=1&
include_adult=false`;
This code is invalid. You cannot add line breaks in a URL.
By changing to this, I was able to make it work:
const url = `https://api.themoviedb.org/3/search/movie?api_key=api_key&language=en-US&query=${query}&page=1&include_adult=false`;
first of all,
never share your personal api key on the internet!!
const data = await res.json();
Why use await again when you have already fetched the data?
also, if the response is json, it's JSON.parse(res).
Next, have you actually looked at the data you are getting?
Try console logging it.
From what I can tell, it's an object, not an array, so you can't use map.

Categories

Resources