How to display images from a JSON URL array in React - javascript

I have converted a JSON endpoint into a JavaScript array and I've mapped through it to get the key values I need. 3 out of 4 are text values but the first one is an image and it just displays the URL link. I have tried to map through the same array and display just the images and it works but then I cannot merge the two elements into one div.
The code:
export default function Pokes() {
const [pokemonData, setPokemonData] = React.useState({});
React.useEffect(() => {
fetch(
"https://raw.githubusercontent.com/Biuni/PokemonGO-Pokedex/master/pokedex.json"
)
.then((res) => res.json())
.then((data) => setPokemonData(data.pokemon));
}, []);
const allPokes = pokemonData;
const pokemons = Object.values(allPokes);
const pokesData = pokemons.map(pokemon => `${pokemon.img} ${pokemon.num} ${pokemon.name} ${pokemon.type}`);
let renderedOutput = pokesData.map(item => <div className="infodiv" style={{ flex: 1, flexBasis: "33%" }}> {item} </div>)
return (
<main>
<div>
<div style={{ display: "flex", flexWrap: "wrap" }}>{renderedOutput}</div>
</div>
</main>
);
}

const pokesData = pokemons.map(pokemon => `${pokemon.img} ${pokemon.num} ${pokemon.name} ${pokemon.type}`)
This line of code would return "image url number name", what you actually want is the real image which requires the use of the img HTML tag. Implementing this with your code, it would become:
export default function Pokes() {
const [pokemonData, setPokemonData] = React.useState({});
React.useEffect(() => {
fetch(
"https://raw.githubusercontent.com/Biuni/PokemonGO-Pokedex/master/pokedex.json"
)
.then((res) => res.json())
.then((data) => setPokemonData(data.pokemon));
}, []);
const allPokes = pokemonData;
const pokemons = Object.values(allPokes);
let renderedOutput = pokemons.map(pokemon => <div className="infodiv" style={{ flex: 1, flexBasis: "33%" }}> <img src={pokemon.img} /> {pokemon.num} {pokemon.name} </div>)
// Note the code change above ^^^
return (
<main>
<div>
<div style={{ display: "flex", flexWrap: "wrap" }}>{renderedOutput}</div>
</div>
</main>
);
}

Here is the solution if that is what you are looking after. Here is codesandbox for below code;
import { useState, useEffect, useCallback } from "react";
import axios from "axios";
const URI =
"https://raw.githubusercontent.com/Biuni/PokemonGO-Pokedex/master/pokedex.json";
const App = () => {
const [values, setValues] = useState([]);
const getPokomonGo = useCallback(async () => {
try {
const { data } = await axios.get(URI);
if (data) setValues(data?.pokemon);
} catch (err) {
console.log({ err });
}
}, []);
useEffect(() => {
getPokomonGo();
}, [getPokomonGo]);
return (
<div className="App">
<h1>Pokemon Images</h1>
{values &&
values.map(({ num, name, img }) => (
<img src={img} alt={name} key={num} />
))}
</div>
);
};
export default App;

<img src={{item.img}} alt="Lamp" width="100" height="100">

Related

How to substitute values ​in a component from two arrays?

I have an array of houses that comes from Firestore, and an array of images of those houses that come from Storage. With the help of map, I go through the array of houses and supply information about it to the component. How do I iterate through an array of images and add them to a component?
const List = ({ selectedHouse }) => {
const [houseTitles, setHouseTitle] = useState([]);
const [houseImages, setHouseImages] = useState([]);
const imageListRef = ref(storage, "images/");
useEffect(() => {
const q = query(collection(db, "map-markers"));
onSnapshot(q, (querySnapshot) => {
setHouseTitle(
querySnapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
}))
);
});
}, []);
useEffect(() => {
listAll(imageListRef).then((response) => {
response.items.forEach((item) => {
getDownloadURL(item).then((url) => {
setHouseImages((prev) => [...prev, url]);
});
});
});
}, []);
return (
<div className="list-container" style={{ width: "50%" }}>
<ListItem title={houseTitles[selectedHouse]?.data?.title} />
{houseTitles
.filter((title, index) => index !== selectedHouse)
.map((title, index) => (
<ListItem key={index} title={title?.data?.title} />
))}
</div>
);
};
const ListItem = (props) => {
return (
<div className="list-item">
<img src={props.url} alt="" />
<h2>{props.title}</h2>
</div>
);
};
I think this is the answer you are looking for:
return (
// ...
{houseTitles
.map((title, idx) => ({title, image: houseImages[idx]}))
.filter((_houseArr, idx) => idx !== selectedHouse)
.map((houseArr, idx) => (
<ListItem key={idx} title={houseArr.title?.data?.title} url={/* YOUR IMAGE SRC HERE, IS IT houseArr.image? */} />
))}
// ...
)

Why my code is not fetching updated details from API's even after changing state in React JS?

I am using two components: LandingPage and SearchMovie. SearchMovie component is updating searchTerm (onChange) and passing it to LandingPage(Parent component) which is fetching movies from API. I checked in console.log and SearchTerm state is updating but LandingPage is not re rendering with updated state of searchTerm. How can I do it? I am posting the code here:
**
LandingPage code:
**
import React, { useEffect, useState, useRef } from 'react'
import { Typography, Row } from 'antd';
import { API_URL, API_KEY, IMAGE_BASE_URL, IMAGE_SIZE, POSTER_SIZE } from '../../Config'
import MainImage from './Sections/MainImage'
import GridCard from '../../commons/GridCards'
import SearchMenu from '../LandingPage/Sections/SearchMenu'
const { Title } = Typography;
function LandingPage(props) {
const [searchTerm, setSearchTerm] = useState('');
console.log("searchIitialTerm = " + searchTerm);
const buttonRef = useRef(null);
const [Movies, setMovies] = useState([])
const [MainMovieImage, setMainMovieImage] = useState(null)
const [Loading, setLoading] = useState(true)
const [CurrentPage, setCurrentPage] = useState(0)
console.log(props.SearchMenu);
console.log("searchTermLanding = " + searchTerm);
var path;
var loadpath;
onchange = (searchTerm) => {
if (searchTerm != '') {
path = `${API_URL}search/movie?api_key=${API_KEY}&language=en-US&query=${searchTerm}&page=1`;
loadpath = `${API_URL}search/movie?api_key=${API_KEY}&language=en-US&query=${searchTerm}&page=${CurrentPage + 1}`;
}
else if (searchTerm == '') {
path = `${API_URL}movie/popular?api_key=${API_KEY}&language=en-US&page=1`;
loadpath = `${API_URL}movie/popular?api_key=${API_KEY}&language=en-US&page=${CurrentPage + 1}`;
}
}
useEffect(() => {
const endpoint = path;
fetchMovies(endpoint)
}, [])
useEffect(() => {
window.addEventListener("scroll", handleScroll);
}, [])
const fetchMovies = (endpoint) => {
fetch(endpoint)
.then(result => result.json())
.then(result => {
// console.log(result)
// console.log('Movies',...Movies)
// console.log('result',...result.results)
setMovies([...Movies, ...result.results])
setMainMovieImage(MainMovieImage || result.results[0])
setCurrentPage(result.page)
}, setLoading(false))
.catch(error => console.error('Error:', error)
)
}
const loadMoreItems = () => {
let endpoint = '';
setLoading(true)
console.log('CurrentPage', CurrentPage)
endpoint = loadpath;
fetchMovies(endpoint);
}
const handleScroll = () => {
const windowHeight = "innerHeight" in window ? window.innerHeight : document.documentElement.offsetHeight;
const body = document.body;
const html = document.documentElement;
const docHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
const windowBottom = windowHeight + window.pageYOffset;
if (windowBottom >= docHeight - 1) {
// loadMoreItems()
console.log('clicked')
buttonRef.current.click();
}
}
return (
<div>
<div className="menu__container menu_search">
<SearchMenu mode="horizontal" onChange={value => setSearchTerm(value)} />
</div>
<div style={{ width: '100%', margin: '0' }}>
{MainMovieImage &&
<MainImage
image={`${IMAGE_BASE_URL}${IMAGE_SIZE}${MainMovieImage.backdrop_path}`}
title={MainMovieImage.original_title}
text={MainMovieImage.overview}
/>
}
<div style={{ width: '85%', margin: '1rem auto' }}>
<Title level={2} > Latest movies </Title>
<hr />
<Row gutter={[16, 16]}>
{Movies && Movies.map((movie, index) => (
<React.Fragment key={index}>
<GridCard
image={movie.poster_path ?
`${IMAGE_BASE_URL}${POSTER_SIZE}${movie.poster_path}`
: null}
movieId={movie.id}
movieName={movie.original_title}
/>
</React.Fragment>
))}
</Row>
{Loading &&
<div>Loading...</div>}
<br />
<div style={{ display: 'flex', justifyContent: 'center' }}>
<button ref={buttonRef} className="loadMore" onClick={loadMoreItems}>Load More</button>
</div>
</div>
</div>
</div>
)
}
export default LandingPage
**
SearchMenu code:
**
import React, { useState } from 'react';
import { Route, Switch } from "react-router-dom";
import { Menu } from 'antd';
import { Input } from 'antd';
//import LandingPage from '../../LandingPage/LandingPage';
import '../../NavBar/Sections/Navbar.css';
const SearchMenu = (props) => {
console.log("props = " + props);
const [searchTerm, setSearchTerm] = useState("");
const { Search } = Input;
const onSearch = value => console.log(value);
function searchChangeHandler(e) {
e.preventDefault();
console.log(e.target.value);
setSearchTerm(e.target.value);
props.onChange(e.target.value);
}
console.log("searchTerm = " + searchTerm);
//console.log(props.onChange);
return (
<div className="searchMenu">
<Menu mode={props.mode} />
<Search
placeholder="Search"
allowClear onSearch={onSearch}
style={{ width: 400 }}
onChange={(e) => searchChangeHandler(e)}
/>
{/*
console.log("Search Term = " + searchTerm);
<LandingPage search={searchTerm}/>
*/}
</div>
)
}
export default SearchMenu;
The searchTerm state in LandingPage changes, but this doesn't trigger any updates to the API data. You defined an onchange function for the search term, but you haven't called it anywhere.
You can redo the search on every keystroke, or you can respond to clicks of a search button and onPressEnter in your search input. I'm going to redo the search on every change. So we've already got searchTerm updating -- we just need to use it!
I think it makes sense to set currentPage before loading data rather than after, but that's just my opinion. That way the effect can respond to changes in the page and the query.
Try this:
function LandingPage() {
const [searchTerm, setSearchTerm] = useState("");
const [movies, setMovies] = useState([]);
const [mainMovieImage, setMainMovieImage] = useState(null);
const [loading, setLoading] = useState(true);
const [currentPage, setCurrentPage] = useState(1);
// do you need this? could just call loadMoreItems() instead of click()
const buttonRef = useRef(null);
const loadMoreItems = () => {
// just set the page, the effect will respond to it
setCurrentPage((page) => page + 1);
};
const onChangeSearch = (value) => {
// reset page to 1 when changing search
setSearchTerm(value);
setCurrentPage(1);
};
// run effect to load movies when the page or the searchTerm changes
useEffect(() => {
const endpoint =
searchTerm === ""
? `${API_URL}movie/popular?api_key=${API_KEY}&language=en-US&page=${currentPage}`
: `${API_URL}search/movie?api_key=${API_KEY}&language=en-US&query=${encodeURIComponent(
searchTerm
)}&page=${currentPage}`;
// could use async/await but promise/then is fine too
setLoading(true);
fetch(endpoint)
.then((response) => response.json())
.then((json) => {
// replace state on page 1 of a new search
// otherwise append to exisiting
setMovies((previous) =>
currentPage === 1 ? json.results : [...previous, ...json.results]
);
// only replace if not already set
// when should we reset this?
setMainMovieImage((previous) => previous || json.results[0]);
})
.catch((error) => console.error("Error:", error))
.finally(() => setLoading(false));
}, [searchTerm, currentPage]);
const handleScroll = () => {
const windowHeight =
"innerHeight" in window
? window.innerHeight
: document.documentElement.offsetHeight;
const body = document.body;
const html = document.documentElement;
const docHeight = Math.max(
body.scrollHeight,
body.offsetHeight,
html.clientHeight,
html.scrollHeight,
html.offsetHeight
);
const windowBottom = windowHeight + window.pageYOffset;
if (windowBottom >= docHeight - 1) {
// loadMoreItems()
console.log("clicked");
buttonRef.current?.click();
}
};
useEffect(() => {
window.addEventListener("scroll", handleScroll);
// cleanup function
return () => window.removeEventListener("scroll", handleScroll);
}, []);
return (
<div>
<div className="menu__container menu_search">
<SearchMenu
mode="horizontal"
value={searchTerm}
onChange={onChangeSearch}
/>
</div>
<div style={{ width: "100%", margin: "0" }}>
{mainMovieImage && (
<MainImage
image={`${IMAGE_BASE_URL}${IMAGE_SIZE}${mainMovieImage.backdrop_path}`}
title={mainMovieImage.original_title}
text={mainMovieImage.overview}
/>
)}
<div style={{ width: "85%", margin: "1rem auto" }}>
<Title level={2}> Latest movies </Title>
<hr />
<Row gutter={[16, 16]}>
{movies &&
movies.map((movie, index) => (
<React.Fragment key={index}>
<GridCard
image={
movie.poster_path
? `${IMAGE_BASE_URL}${POSTER_SIZE}${movie.poster_path}`
: null
}
movieId={movie.id}
movieName={movie.original_title}
/>
</React.Fragment>
))}
</Row>
{loading && <div>Loading...</div>}
<br />
<div style={{ display: "flex", justifyContent: "center" }}>
<button
ref={buttonRef}
className="loadMore"
onClick={loadMoreItems}
disabled={loading} // disable button when fetching results
>
Load More
</button>
</div>
</div>
</div>
</div>
);
}
I would make SearchMenu into a controlled component that both reads and updates the searchTerm state from LandingPage instead of having its own state.
const SearchMenu = ({ mode, value, onChange }) => {
return (
<div className="searchMenu">
<Menu mode={mode} />
<Search
value={value}
placeholder="Search"
allowClear
style={{ width: 400 }}
onChange={(e) => onChange(e.target.value)}
/>
</div>
);
};

State not updating in different component

I have a searchbar component that I used context to import into another component. The state of the searchbar in its own component works but when I use the context to import it to another component it does not work. I have used other contexts in my project and they have worked but the searchbar state doesn't. I have no idea where to start, or how to go about fixing it. Can someone point me in the right direction?
export const SearchInput = () => {
const [searchInput, setSearchInput] = useState('');
const handleSubmit = (e) => {
e.preventDefault()
}
return (
<div>
<form onSubmit={handleSubmit}>
<input type='text'
className='search-input'
name='search-movies'
value={searchInput}
onChange={(e) => setSearchInput(e.target.value)} />
</form>
</div>
)
}
//Use Context Component
export const SearchContext = React.createContext()
export function SearchProvider({ children }) {
const [searchInput, setSearchInput] = useState('');
const value = {
searchInput
}
return (
<div>
<SearchContext.Provider value={value}>
{children}
</SearchContext.Provider>
</div>
)
}
const Movies = () => {
const { data, loading, isErr } = useFetch([
`https://api.themoviedb.org/3/list/7077601?api_key=${process.env.REACT_APP_API_KEY}&language=en-US`,
`https://api.themoviedb.org/3/list/7078334?api_key=${process.env.REACT_APP_API_KEY}&language=en-US`,
`https://api.themoviedb.org/3/list/7078244?api_key=${process.env.REACT_APP_API_KEY}&language=en-US`
]);
const { watchList, handleClick } = useContext(WatchListContext);
const { searchInput } = useContext(SearchContext)
const [moviePoster, setmoviePoster] = useState(`giphy (1).gif`);
const [movieTitle, setmovieTitle] = useState('');
const [movieDescription, setmovieDescription] = useState('')
const styles = {
backgroundImage: `url(${moviePoster})`
};
SwiperCore.use([Navigation, Pagination, Scrollbar, A11y]);
return (
<div className='movie-container'>
{isErr && <div className="error">{isErr}</div>}
{loading && <Spinner animation="border" variant="secondary" className="spinner" >
<span>Loading...</span>
</Spinner>}
<div className='movie-hero' style={styles}></div>
<div className="contains-descriptions">
<h2 className="hero-movie-title show-movie">{movieTitle}</h2>
<p className="hero-movie-description show-movie">{movieDescription}</p>
</div>
<section className="movies">
<h2 style={{ color: 'white', marginLeft: '20px' }}>Action </h2>
{data && <Swiper
spaceBetween={10}
slidesPerView={6}
pagination={{ clickable: true }}
scrollbar={{ draggable: true }}
onSlideChange={() => console.log('slide change')}
onSwiper={(swiper) => console.log(swiper)}
>
{data && data[0].items.map(movie =>
<SwiperSlide key={movie.id}>
<div className='movie' >
<img onMouseOver={() => {
setmoviePoster(`${"https://image.tmdb.org/t/p/original" + movie.poster_path}`);
setmovieTitle(movie.original_title);
setmovieDescription(movie.overview);
}}
src={'https://image.tmdb.org/t/p/original' + movie.poster_path} width='250' height='300'
alt='Promotional Poster For Movie'
/>
<button className="watchlist-btn"
onClick={() => handleClick(movie.original_title)}>
{watchList.includes(movie.original_title) ?
<i className="fas fa-minus-circle"></i> :
<i className="fas fa-plus-circle"></i>}
</button>
</div>
</SwiperSlide>
)
}
</Swiper>}
</section>
I'm assuming a component tree that looks something like this:
+-- SearchProvider
| +-- SearchInput
| +-- Movies
Your SearchProvider should be providing both the state and the state setter as its value:
export const SearchContext = React.createContext()
export function SearchProvider({ children }) {
const [searchInput, setSearchInput] = useState('');
const value = {
searchInput,
setSearchInput
};
return ...
}
Your SearchInput should no longer be controlling its own local state. That state now has to be shared with the rest of the tree. Instead, it subscribes to the context and updates it directly:
export const SearchInput = () => {
const { searchInput, setSearchInput } = React.useContext(SearchContext);
const handleSubmit = (e) => {
e.preventDefault()
};
return ...
}
Why are you using context and not just useState and props?
I think it would work with something like the following:
export const SearchInput = (props) => {
const handleSubmit = (e) => {
e.preventDefault()
}
return (
<div>
<form onSubmit={handleSubmit}>
<input type='text'
className='search-input'
name='search-movies'
value={props.value}
onChange={(e) => props.onChange(e.target.value)} />
</form>
{props.children}
</div>
)
}
export function SearchCompoent({ children }) {
const [searchInputValue, setSearchInputValue] = useState('');
return (
<SearchInput value={searchInputValue}>
{children}
</SearchInput>
)
}

How to write if/ else function?

I have a function for my shopping cart that displays the product list when "data" is passed to it, but when there is no "data" available, I would like the cart to return "Empty Cart". Can I do this with a if/else statement? The code is below.
const propductList = (data) =>{
console.log(data)
setTotal(data)
}
I guess what I want to do is something like this
const productList = (data) =>{
console.log(data)
if (data) setTotal
} else return "Empty Cart"
}
The full code is below:
import React, { useState } from 'react'
import { useDispatch } from 'react-redux';
import {
removeCartItem,
onSuccessBuy
} from '../../../_actions/user_actions';
import UserCardBlock from './Sections/UserCardBlock';
import { Result, Empty, Button } from 'antd';
import Paypal from '../../utils/Paypal';
function CartPage(props) {
const dispatch = useDispatch();
console.log(props)
const [Total, setTotal] = useState(props.location.state.data.price)
const [ShowTotal, setShowTotal] = useState(true)
const [ShowSuccess, setShowSuccess] = useState(false)
const removeFromCart = (productId) => {
dispatch(removeCartItem(productId))
}
const transactionSuccess = (data) => {
dispatch(onSuccessBuy({
cartDetail: props.user.cartDetail,
paymentData: data
}))
.then(response => {
setShowSuccess(true)
setShowTotal(false)
}
)
}
const transactionError = () => {
console.log('Paypal error')
}
const transactionCanceled = () => {
console.log('Transaction canceled')
}
const propductList = (data) =>{
console.log(data)
setTotal(data)
}
return (
<div style={{ width: '85%', margin: '3rem auto' }}>
<h1>My Cart</h1>
<div>
{props.location.state.data &&
<UserCardBlock
productData={props.location.state.data}
removeItem={removeFromCart}
productList={data => propductList(data)}
/>
}
{ShowTotal ? (
<div style={{ marginTop: "3rem" }}>
<h2>Total amount: ${Total * 15} </h2>
</div>
) : ShowSuccess ? (
<Result status="success" title="Successfully Purchased Items" />
) : (
<div
style={{
width: "100%",
display: "flex",
flexDirection: "column",
justifyContent: "center",
}}
>
<br />
<Empty description={false} />
<p>No Items In The Cart</p>
</div>
)}
</div>
{/* Paypal Button */}
{ShowTotal &&
<Paypal
toPay={Total}
onSuccess={transactionSuccess}
transactionError={transactionError}
transactionCanceled={transactionCanceled}
/>
}
</div>
)
}
export default CartPage
You can do it like this:
const productList = (data) =>{
if (data) return setTotal(data)
else return "Empty Cart"
}
Or even shorter in a ternary operator:
const productList = (data) =>{
return data ? setTotal(data) : 'Empty Cart';
}
Or even shorter directly doing the ternary as return:
const productList = data => data ? setTotal(data) : 'Empty Cart';

Displaying Nested Objects in React after fetching the data from an api?

The user data from json placeholder has the address as an object inside of the user object and I am trying to figure out how to display that data for learning and understanding purposes
import React, { useState, useEffect } from "react";
import "./App.css";
const App = () => {
const [users, setUsers] = useState([]);
const [user, setUser] = useState({});
useEffect(() => {
getUsers();
getUser();
//eslint-disable-next-line
}, []);
const getUsers = async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/users");
const data = await res.json();
setUsers(data);
};
const getUser = async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/users/1");
const data = await res.json();
setUser(data);
console.log(data);
};
return (
<div className="App">
<h3 style={{ marginBottom: "5px" }}>
Getting an Array of users from an Api
</h3>
<ul>
{users.map(user => (
<li style={{ marginBottom: "5px", paddingLeft: "10px" }}>
Name:{user.name} <br />
Email:{user.email}
</li>
))}
</ul>
<div className="card" style={{ width: "400px" }}>
<h3>Name: {user.name}</h3>
<p>Email: {user.email}</p>
</div>
</div>
);
};
export default App;
to this point everything works just fine
when I try to access the user address it throws an error
<div className="card" style={{ width: "400px" }}>
<h3>Name: {user.name}</h3>
<p>Email: {user.email}</p>
<p>Address: {user.address.street}
</div>
How do I access and object within an object?
You have to set init user.
Because of rendering null issue.
const defaultUser = {
name: "",
email: "",
address: {
street: ""
}
};
const [users, setUsers] = useState([defaultUser]);
const [user, setUser] = useState(defaultUser);

Categories

Resources