this is my row.js code:
import React, { useEffect, useState } from 'react';
import axios from './axios';
const base_URL="https://image.tmdb.org/t/p/original";
function Row({title,fetchUrl}) {
const [movies,setMovies]=useState([]);
useEffect(()=>{
async function fetchData(){
const request=await axios.get(fetchUrl);
console.log(request);
setMovies(request.data.results);
return request;
}
fetchData();
},[fetchUrl]);
return (
<div className="row">
<h2>{title}</h2>
<div className="row__posters">
{movies.map(movie=>{
console.log(movie);
<img src={'${base_URL}${movie.poster_path}'} alt={movie.name}/>
})}
</div>
</div>
)
}
export default Row
My screen should display the posters of films but its not displaying it, I wonder there is some problem in movies.map...... part and img src="......" part
You need to return image:
<div className="row__posters">
{movies.map(movie=>{
console.log(movie);
return <img src={'${base_URL}${movie.poster_path}'} alt={movie.name}/>
})}
</div>
you only map the movies object instead returning .Hence try this:
{movies.map((movie,index)=>{
console.log(movie);
return ( <img key={index} src={'${base_URL}${movie.poster_path}'} alt={movie.name}/>)
})}
here , also use key = {index} within img tag.It gives the unique key to each img tag iteratively .
Related
I have two components "search" and "Maindata". I am passing the input value from the search component to maindata component where I want to replace the city attribute with the input value(location) in API. but the browser display went blank and the console give an undefined 'city' error, etc. I got stuck in this problem if anyone has a solution?
Here "search" component;
import React , {useState} from "react";
import Maindata from "./Maindata";
import "../Componentstyle/search.css";
export default function Search() {
const [location, setLocation] = useState();
<Maindata city={location}/>
return (
<div className="main">
<nav className="istclass">
<form className="form">
<div className="search">
<input
value={location}
placeholder="search city"
className="searchbox"
onChange={(e)=>setLocation(e.target.value)}
/>
<button className="nd" onClick={(e)=>setLocation(e.target.value)}>
Submit
</button>
</div>
</form>
</nav>
</div>
);
}
Here "Maindata" component;
import React, { useState, useEffect } from "react";
import "../Componentstyle/Main.css";
export default function Maindata(props) {
const [data, setData] = useState(null);
let city = console.log(props.city);
let weather = async () => {
const key = "1ab6ef20384db1d7d9d205d609f7eef0";
await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${key}&units=metric&formatted=0`
)
.then((response) => response.json())
.then((actualData) => setData(actualData));
};
useEffect(() => {
weather();
}, []);
if (!data) {
return <div>Loading...</div>;
}
const link = `http://openweathermap.org/img/w/${data.weather[0].icon}.png`;
return (
<div className="maindata">
<div className="city">{data.name}</div>
<div className="temp">{data.main.temp} C</div>
<div className="icon">
<img src={link} alt="not found" />{" "}
</div>
<div className="feel">feels Like {data.main.feels_like} C</div>
<div className="wind">Wind {data.wind.speed} Km/hr</div>
<div className="cloudy">{data.weather[0].main}</div>
<div className="humidity">humidity {data.main.humidity}%</div>
<div className="sunrise">
sunrise :- {new Date(data.sys.sunrise * 1000).toUTCString()}{" "}
</div>
<div className="sunset">
sunset :- {new Date(data.sys.sunset * 1000).toUTCString()}
</div>
</div>
);
}
<Maindata city={location}/>
keep this line of code inside the return
In your example, there is no meaningful connection between the Search and Maindata components. Meaning Maindata component will not get rendered on the page because it is not in the return statement of the Search component.
The Maindata component as below, is in JSX format, when you use JSX in your code in React, under the hood, React.createElement() method is being called.
Each call to React.createElement returns an object describing what to render to that part of the page. So it makes sense to put the Maindata component in the return statement. That is responsible for rendering the HTML elements from that component when you're loading a page containing that component.
<Maindata city={location}/> // is JSX and should be in the return statement to get rendered on the page and showing the right location
I'm learning React by making a Spotify clone and for now, what I'm trying to do is to show Spotify sections such as "last played songs", "top artists" and "top songs" through a component called Body.js.
I get the data from a Spotify official API library created by jmperez in a useEffect hook in the App.js component. Once I get the data from the API, I store it in an object called initialState in a file called reducer.js.
This reducer.js file contains the initial state and the reducer function for a custom hook called useDataLayer.js which is a useContext hook that passes as value a useReducer to all the branches of my program. This way what I do is update the initialState from App.js and access this object through the useDataLayer hook in the different branches of my program (among them the Body component).
The problem I am having now is that it is not rendering the three sections mentioned before in Spotify, but only shows me one which is the "top songs". The weird thing is that for a second it does render the other components as if it was getting the data and rendering but then it updates and they disappear. Please if someone can help me with this problem and explain to me why this happens it would be great.
App.js code
import React, {useState, useEffect} from 'react';
import Login from './components/Login';
import Player from './components/Player';
import { getTokenFromResponse } from './spotify';
import './styles/App.scss';
import SpotifyWebApi from "spotify-web-api-js";
import { library } from '#fortawesome/fontawesome-svg-core';
import { fas } from '#fortawesome/free-solid-svg-icons';
import { fab } from '#fortawesome/free-brands-svg-icons';
import { useDataLayer } from './components/Hooks/useDataLayer';
library.add(fas, fab);
//spotify library instance
const spotify = new SpotifyWebApi();
function App() {
const [token, setToken] = useState(null);
const [{ user }, dispatch] = useDataLayer();
// where I get the necessary data from the api
useEffect(() => {
// function to get access token
let accessToken = getTokenFromResponse();
window.location.hash = '';
if(accessToken){
spotify.setAccessToken(accessToken);
setToken(accessToken);
//FROM HERE I GET THE DATA I NEED
// here I get the data of my user
spotify.getMe().then((data) =>{
dispatch({
type: "GET_USER",
user: date
})
});
spotify.getUserPlaylists().then((data) => {
dispatch({
type: "GET_PLAYLISTS",
playlists: dates
})
});
spotify.getMyTopTracks({limit: 4}).then((data) => {
dispatch({
type: "GET_TOP_TRACKS",
top_tracks:data,
})
});
spotify.getMyRecentlyPlayedTracks({limit: 4}).then((data) => {
dispatch({
type: "RECENTLY_PLAYED",
recently_played: date,
})
});
spotify.getMyTopArtists({limit: 4}).then((data) => {
dispatch({
type: "GET_TOP_ARTISTS",
top_artists: date,
})
});
}
}, [token])
//if the token is valid enter Player.js where Body.js is inside and if not return to the login component
return (
<div className="App">
{ token ? <Player spotify= {spotify} /> : <Login />}
</div>
);
}
export defaultApp;
Body.js code
import React from 'react'
import '../styles/Body.scss'
import { useDataLayer } from "./Hooks/useDataLayer.js";
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
function Body({ spotify }) {
//get the properties of the necessary data that I want to display in this component with useDataLayer
const [{ spotify_recommendations, recently_played, top_tracks, top_artists }, dispatch] = useDataLayer();
return (
<div className= "main-body">
<div className= "body-option">
<span className= "see-more">See All</span>
<FontAwesomeIcon icon={['fas', 'arrow-right']} />
<div>
{
//to show the image and info of the track
recently_played?.items.map((item, index) =>{
return (
<div className= "track" key= {index}>
<img src= {item.track.album.images[1].url} alt= "recently played track"></img>
<div className= "track-data">
<h3>{item.track.name}</h3>
<p>{item.track.artists.map(artist => artist.name).join(", ")}</p>
</div>
</div>
)
})
}
</div>
</div>
<div className= "body-option">
<span className= "see-more">See All</span>
<FontAwesomeIcon icon={['fas', 'arrow-right']} />
<div>
{
//to show the image and info of the track
top_tracks?.items.map((topArtist, index) => {
return (
<div className= "track" key= {index}>
<img src= {topArtist.album.images[1].url} alt= "recently played track"></img>
<div className= "track-data">
<h3>{topArtist.name}</h3>
<p>{topArtist.artists.map(artist => artist.name).join(", ")}</p>
</div>
</div>
)
})
}
</div>
</div>
<div className= "body-option">
<span className= "see-more">See All</span>
<FontAwesomeIcon icon={['fas', 'arrow-right']} />
<div>
{
//to show the image and info of the artist
top_artists?.items.map((topTrack, index) => {
return (
<div className= "track" key= {index}>
<img src= {topTrack.images[1].url} alt= "recently played track"></img>
<div className= "track-data">
<h3>{topTrack.name}</h3>
<p>{topTrack.genres.join(", ")}</p>
</div>
</div>
)
})
}
</div>
</div>
</div>
)
}
export default Body
Code of my custom hook useDataLayer.js
import React, {useContext, createContext, useReducer} from 'react'
let DataContext = createContext();
export function DataLayer({reducer, initialState, children}) {
return (
<DataContext.Provider value= {useReducer(reducer, initialState)}>
{children}
</DataContext.Provider>
)
}
export let useDataLayer = () => useContext(DataContext);
SideBar.js: the component where i display the user's playlist
import React from 'react';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { useDataLayer } from './Hooks/useDataLayer.js';
import '../styles/SideBar.scss';
function SideBar({ spotify }){
const [{ playlists }, dispatch] = useDataLayer();
return (
<div className= "side-bar">
<div className= "side-bar-options">
<div className= "spotify-logo">
<FontAwesomeIcon icon={['fab', 'spotify']} size= "3x"/>
<h1 className= "spotify-title">Spotify</h1>
</div>
<a className= "option" href= "./Player.js">
<FontAwesomeIcon icon={['fas', 'home']} />
<p className= "option-title" >Inicio</p>
</a>
<a className= "option" href= "./Player.js">
<FontAwesomeIcon icon={['fas', 'search']} />
<p className= "option-title">Buscar</p>
</a>
<a className= "option" href= "./Player.js">
<FontAwesomeIcon icon={['fas', 'headphones']} />
<p className= "option-title" >Tu Biblioteca</p>
</a>
</div>
<p className= "playlist-title">PLAYLISTS</p>
<div className= "playlists">
{
playlists?.items?.map(
(list, index) =>
<p className="playlists-option"
key={index}
>
{list.name}
</p>
)
}
</div>
</div>
)
}
export default SideBar;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Player.js
import React from 'react';
import "../styles/Player.scss";
import SideBar from "./SideBar.js";
import Body from "./Body.js";
function Player({spotify}) {
return (
<div className= "player-container">
<div className= "player_body">
<SideBar spotify= {spotify} />
<Body spotify= {spotify} />
</div>
</div>
)
}
export default Player;
spotify.js: the code where i get the token from the URL
const authEndpoint = "https://accounts.spotify.com/authorize";
const clientId = '84c134b175474ddabeef0e0b3f9cb389'
const redirectUri = 'http://localhost:3000/'
const scopes = [
"user-read-currently-playing",
"user-read-recently-played",
"user-read-playback-state",
"user-top-read",
"user-modify-playback-state",
"user-follow-modify",
"user-follow-read",
"playlist-modify-public",
"playlist-modify-private",
"playlist-read-private",
"playlist-read-collaborative"
];
//obtain acces token from url
export const getTokenFromResponse = () => {
let params = new URLSearchParams(window.location.hash.substring(1));
let token = params.get("access_token");
return token;
};
//acces url
const accessUrl = `${authEndpoint}?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scopes.join("%20")}&response_type=token&show_dialog=true`;
export default accessUrl;
Thank you very much for your time and attention.
By the look of it, your useEffect hook is being called twice in quick succession. It has the dependency of token, which starts as null for your first render. Inside the hook you then read the accessToken and set the state for it to your token. Doing this will trigger another render because the dependency changed from null to the value of your access token.
I would suggest to simply remove token from your useEffect dependency array (so that your hook acts as an "on mounted" event) and see if that gets you the desired effect. You should eventually move the getTokenFromResponse outside the hook, assuming it is not available on first render, as I'm just guessing it is immediately available.
Post comments, it might be better to separate your initializing code into a useEffect that only runs once, when the component is first mounted. This way, as I now suspect getTokenFromResponse() returns a new Object every call, you only need to call it once. Note, if it returns a Promise, this won't work, so you should verify this first before trying.
To better illustrate my point, I've put it in code with some comments:
useEffect(() => {
setToken(getTokenFromResponse());
// this only needs to happen once
window.location.hash = '';
}, []; // <- empty dependency array will only run once, on mount
useEffect(() => {
// out of the 2 times this will be called,
// it should only pass this condition on the last/2nd
if(token){
spotify.setAccessToken(token);
//FROM HERE I GET THE DATA I NEED
// here I get the data of my user
spotify.getMe().then((data) =>{
dispatch({
type: "GET_USER",
user: date
})
});
spotify.getUserPlaylists().then((data) => {
dispatch({
type: "GET_PLAYLISTS",
playlists: dates
})
});
spotify.getMyTopTracks({limit: 4}).then((data) => {
dispatch({
type: "GET_TOP_TRACKS",
top_tracks:data,
})
});
spotify.getMyRecentlyPlayedTracks({limit: 4}).then((data) => {
dispatch({
type: "RECENTLY_PLAYED",
recently_played: date,
})
});
spotify.getMyTopArtists({limit: 4}).then((data) => {
dispatch({
type: "GET_TOP_ARTISTS",
top_artists: date,
})
});
}
// this will trigger the effect any time token changes,
// which should now only be twice:
// 1st at it's default state value (null)
// 2nd after the previous useEffect sets its state
}, [token]);
Hope that helps but, if it's a promise, you'll need to handle it according (suggestion: moving the setToken into the then)
Good luck!
i'm learning the react techologie, i'm the first exercice i must put the images like this, but when i use the map function to put the images i don't saw the images :/
image dynamics example
import React from 'react'<br>
import './Style/App.css'
const tabImg = ['./img/html.png', './img/css.png', './img/javascript.png', './img/logo192.png']<br>
const displayImgTech = tabImg.map((tech) => <img key={tech} src={tech} alt="techno"/>)<br>
export const Header = () => {
return (
<div className='container'>
<img src={displayImgTech} alt='technoFE'/>
</div>
)
}
Thank you
By passing displayImgTechin the src img props your are not adding an url but an "array" of img tags. so you just need to call {display take in your div like this:
const displayImgTech = tabImg.map((tech) => <img key={tech} src={tech} alt="techno"/>)<br>
export const Header = () => {
return (
<div className='container'>
{displayImgTech}
</div>
)
}
I'm new in React and I'm doing a little app with PokeAPI. I have a component called PokemonDetail in which I want to show the details of a pokemon, but the app throws me the next error
TypeError: Cannot read property 'front_default' of undefined
my component looks like this:
import React from "react";
const PokemonDetail = ({ pokemon }) => {
return (
<div>
<div className="text-center">{pokemon.name}</div>
<img src={pokemon.sprites.front_default} alt={pokemon.name} />
{pokemon.id}
</div>
);
};
export default PokemonDetail;
And the App component from which the PokemonDetail recive the prop of pokemon looks like this:
import React from "react";
import PokeAPI from "../apis/PokeAPI";
import SearchBar from "./SearchBar";
import PokemonDetail from "./PokemonDetail";
class App extends React.Component {
state = { pokemon: '' };
onTermSubmit = async term => {
try {
const response = await PokeAPI.get(`pokemon/${term}`);
this.setState({ pokemon: response.data });
console.log(response);
} catch (error) {
console.log("No existe");
}
};
render() {
return (
<div className="container">
<div className="row mt-3">
<div className="col">
<SearchBar onFormSubmit={this.onTermSubmit} />
</div>
</div>
<div className="row mt-3">
<div className="col-9" />
<div className="col-3">
<PokemonDetail pokemon={this.state.pokemon} />
</div>
</div>
</div>
);
}
}
export default App;
I don't understand why it throws me this error because only throws it with this and other properties of the json. With the name property works and wait until I send it some props, same with the id but no with the front_default property, which is a url of a image.
Because ajax is slower than react rendering, you can use a loading component before you get the data.
const PokemonDetail = ({ pokemon }) => {
if(pokemon.sprites == undefined){
return(
<div>
Loading...
</div>
);
}
return (
<div>
<div className="text-center">{pokemon.name}</div>
<img src={pokemon.sprites.front_default} alt={pokemon.name} />
{pokemon.id}
</div>
);
};
Very likely just an AJAX issue, your component renders before it has time to complete your request to the API. Try adding an additional check before rendering the image.
import React from "react";
const PokemonDetail = ({ pokemon }) => {
return (
<div>
<div className="text-center">{pokemon.name}</div>
{pokemon.sprites ? (
<img src={pokemon.sprites.front_default} alt={pokemon.name} />
) : (
null
)
}
{pokemon.id}
</div>
);
};
export default PokemonDetail;
#ZHAOXIANLONG gave you the best solution (use a loading component until you receive data), but, if you do not use a loading component, you can use the get method from lodash library [1] in order to avoid a possible error.
import React from "react";
import _ from 'lodash';
const PokemonDetail = ({ pokemon }) => {
const front_default = _.get(pokemon, 'sprites.front_default', 'DEFAULT_VALUE');
const name = _.get(pokemon, 'name', 'DEFAULT_VALUE');
return (
<div>
<div className="text-center">{pokemon.name}</div>
<img src={pokemon.sprites.front_default} alt={pokemon.name} />
{pokemon.id}
</div>
);
};
export default PokemonDetail;
where the third parameter ('DEFAULT_VALUE') is a default value that will be used if the lodash can not retrieve a value for your query.
PS: I advise you to use lodash even in #ZHAOXIANLONG solution if you know that your API Server can be changed.
[1] https://lodash.com/docs/4.17.11#get
The initial state is { pokemon: '' }; pokemon is an empty string. PokemonDetail is referring to pokemon.sprites.front_default, but pokemon is initially a string and a string does not have a field called sprites.
If you are expecting pokemon to eventually become an object, you could initialize it to something that looks like an object:
state = { pokemon: { sprites: {front_default: '' }}};
I got access to rest of properties instead of one. I didnt catch what problem is.
I've tried:
this.props.data.imageLinks.thumbnail
this.props.data.imageLinks[thumbnail]
this.props.data.imageLinks["thumbnail"]
But other properties spew out correct value when I tried: {this.props.data.title}, {this.props.data.author}.
class Book extends React.Component {
render() {
console.log('prop',this.props.data.imageLinks)
return (
<div key={this.props.data.id}>
<div className="book">
<div className="book-top">
<div
className="book-cover"
style={{
width: 128,
height: 192,
backgroundImage: `url(${this.props.data.imageLinks.thumbnail})`
}}
></div>
<DropDownList/>
</div>
<div className="book-title">{this.props.data.title}</div>
<div className="book-authors">{this.props.data.author}</div>
</div>
<div>
{/*BooksAPI.getAll().then(function(res){console.log(res)})*/}
</div>
</div>
)
}
}
This object how its look
I guess, thumbnail comes after component mount. You should check first, if there is a thumbnail use thumbnail as a background image or wait until thumbnail load
I have got a new method of making this work. Basically I made a different component for rendering, and passed properties through props. I then used try and catch in the component where i map the array, and before sending the props, I try for error, if there's no error, it passes normally image links. if there's an error, I would pass image property as "", which basically means empty string in img tag. I will attach the code. its rough code, with console.logs, but main thing is inside the try and catch
main driver code
import React, { useEffect, useState } from 'react';
import Details from './bookDetails';
import './books.css'
export default function Books2() {
const [bookName, setName] = useState("a");
const [load, setLoad] = useState(false)
const [itemArray, setArray] = useState([]);
useEffect(() => {
loadData();
}, [])
async function loadData() {
setLoad(true);
// console.log(bookName)
await fetch("https://www.googleapis.com/books/v1/volumes?q=intitle:"+bookName)
.then(response => response.json())
.then(data => setArray(data.items))
setLoad(false)
}
function handleChange(event) {
// console.log(event.target.value)
setName(event.target.value)
}
function handlePress() {
loadData();
}
// console.log(itemArray)
var className = (load===true) ? "loading" : "null"
return (
<div>
<h1>Hello</h1>
<input placeholder="book name" value={bookName} onChange={handleChange} />
<button onClick={handlePress}>Search</button>
<h3 className={className}>Loading...</h3>
{/* <input placeholder="name of book" value={} /> */}
{itemArray.map(book => {
{/* console.log(book.volumeInfo.imageLinks) */}
try{
return(
<Details
key={book.id}
bookName={book.volumeInfo.title}
bookYear={book.volumeInfo.publishedDate}
bookDesc={book.volumeInfo.description}
bookLink={book.volumeInfo.infoLink}
bookImg={book.volumeInfo.imageLinks.smallThumbnail}
/>
)}
catch(err) {
<Details
key={book.id}
bookName={book.volumeInfo.title}
bookYear={book.volumeInfo.publishedDate}
bookDesc={book.volumeInfo.description}
bookLink={book.volumeInfo.infoLink}
bookImg={""}
/>
}
})}
{/* <button onClick={handlePress}>Search</button> */}
</div>
)
}
component for rendering code
import React from 'react';
export default function Details(props) {
return(
<div>
<h1>{props.bookName}</h1>
<a href={props.bookLink} target='_blank'>Link to book</a>
<img src={props.bookImg} className="img"/>
<h3>{props.bookYear}</h3>
{/* <h4>{bookPage}</h4> */}
{/* <h5>{bookRating}</h5> */}
<p>{props.bookDesc}</p>
</div>
)
}