I have a react component which maps over an array of movies in the render function, and logging out the image of each one. I'm having trouble mapping out each image itself to an <img /> tag. It currently appears that all of the images are being spit out into the one <img /> tag, so how can I loop through the images and insert each one into its own image tag?
render () {
const movies = this.state.movies.map((movie, i) => {
console.log("Image url: ", movie.images.boxArt)
})
return (
<div className='welcome'>
<div className='container'>
<div className='movies'>
{ this.state.loading &&
<Loader size='10' />
}
{ !this.state.loading &&
<div className='movie'>
<img src={ movies } /> <===SHOULD END UP BEING A ROW OF MOVIE POSTERS
</div>
}
</div>
</div>
</div>
)
}
}
For each array element render an image and specify the src. Like this:
const movies = this.state.movies.map((movie, i) => <img key={i} src={ movie } />)
<div className='movie'>
{ movies }
</div>
Related
I'm doing pokedex (pokemon wiki stuff). I want to change my component view, when clicking on pokemon images (description lookalike). When I click on an image - nothing happens (firstly, I want at least pokemon's name to be added to the pokemonDescription array). What am I doing wrong?
let pokemonDescription = [];
const useForceUpdate = () => {
const [value, setValue] = useState(true);
return () => setValue(value => !value);
}
const forceUpdate = useForceUpdate();
const onPokemonClick = (event) => {
console.log(
"wrapper clicked, event.target - ",
event.target.getAttribute('data-name')
);
pokemonDescription = [];
pokemonDescription.push(event.target.getAttribute('data-name'));
console.log("description array -", pokemonDescription);
forceUpdate();
};
useEffect(() => {
document.querySelector(".wrapper").addEventListener("click", onPokemonClick);
...
return () => {
document.querySelector(".wrapper").removeEventListener("click", onPokemonClick);
};
}, []);
...
return (
<div className="Pokemons">
<div className="column pokemons-list">
<div className="wrapper">
{
pokemonsData.map((p, id) => (
<div className="box" key={ id }>
<img
src={ p.sprites.front_default }
alt="pokemon-img"
title={ p.name }
className="icon"
data-name={p.name}
/>
{ p.name}
<div className="container">
{ pokemonsTypes[id] }
</div>
</div>
))
}
</div>
...
</div>
<div className="column description">
{ pokemonDescription }
</div>
</div>
)
You should add pokemonDescription to your component state
const [pokemonDescription, setPokemonDescription] = useState([]);
Remove the forceUpdate function and hook, it is unnecessary.
Attach the click handlers to the elements with the data-name attribute you are trying to handle.
Map the pokemonDescription state array to renderable JSX. I simply used a div, but you should use whatever your UI design requires.
const onPokemonClick = (event) => {
setPokemonDescription(names => [
...names,
event.target.getAttribute('data-name'),
]);
};
...
return (
<div className="Pokemons">
<div className="column pokemons-list">
<div className="wrapper">
{
pokemonsData.map((p, id) => (
<div className="box" key={ id }>
<img
src={ p.sprites.front_default }
alt="pokemon-img"
title={ p.name }
className="icon"
data-name={p.name}
onClick={onPokemonClick} // <-- attach click handler to img element
/>
{ p.name}
<div className="container">
{ pokemonsTypes[id] }
</div>
</div>
))
}
</div>
...
</div>
<div className="column description">
{pokemonDescription.map(name => (
<div>{name}</div>
))}
</div>
</div>
)
Add pokemonDescription to state instead of some local variable and it will solve your issue.
Try to avoid using forceUpdate, most of the times it means only that you are doing something silly.
I don't what that useForceUpdate does , but here is how would go about adding pokemon names to description array which is a state variable in my answer
const [pokemonDescription , setPokemonDescription ] = useState(null);
const onPokemonClick = (p) => {
const tempPokemonDescription = [...pokemonDescription ];
pokemonDescription.push(p.name);
console.log("description array -", pokemonDescription);
setPokemonDescription(tempPokemonDescription )
};
...
return (
<div className="Pokemons">
<div className="column pokemons-list">
<div className="wrapper">
{
pokemonsData.map((p, id) => (
<div className="box" onClick={e=>onPokemonClick(p)} key={ id }>
<img
src={ p.sprites.front_default }
alt="pokemon-img"
title={ p.name }
className="icon"
/>
{ p.name}
<div className="container">
{ pokemonsTypes[id] }
</div>
</div>
))
}
</div>
...
</div>
<div className="column description">
{ pokemonDescription }
</div>
</div>
)
I am trying to render a child component with images from local folder, but I don't know how to do it.
So I have a const array with details about several projects. Each of the project has its own folder with images. The project name is equal folder name with images
Parent component
import { myProjects } from '../lib/Projects'; //its array with projects
export default class Parent extends Component {
render() {
// function for images
function importAll(r) {
return r.keys().map(r);
}
const projectA = importAll(require.context('../../assets/images/projectA', false, /\.(png|jpe?g|svg)$/));
const projects = myProjects.map((project, i) =>
<Child id={i} key={i} project={project} />)
return (
<div className="main-container">
{projects}
</div>
)
}
}
Child component
export default class Child extends Component {
render() {
const { project } = this.props;
return (
<div className="item">
<div className="desc">
<div className="item-name">
<p>{project.name}</p>
</div>
<div className="item-description">
<p>{project.description}</p>
</div>
<div className="item-tools">
<p>{project.tools}</p>
</div>
</div>
// this part works well
// How to make below part work?
<div className="image-block">
<div className="item-image-first">
<img src={project.name[0]} alt=""/>
</div>
<div className="item-images">
{project.name ? project.name.map((image, index) => {
return (
<div className="image-block-small" key={index}>
<ModalImage
small={image}
large={image}
alt=""
hideDownload={true}
hideZoom={true}
className="modal-image"
/>
</div>
)
})
: null }
</div>
</div>
</div>
)
}
}
Maybe there is a way to add an extra array here?
const projects = myProjects.map((project, i) =>
<Child id={i} key={i} project={project} />)
Any suggestion?
I asked a question here How to join an array with folder images in React, but didn't get any help.
So I was trying to change the logic, unfortunately useless.
I have Parent component with an array. This child component should render that array and somehow images from local folder. Folder name equal project.image.
Child component
export default class Child extends Component {
render() {
const { project } = this.props;
function importAll(r) {
return r.keys().map(r);
}
const folder1 = importAll(require.context('../../assets/images/folder1',false,/\.(png|jpe?g|svg)$/));
const folder2 = importAll(require.context('../../assets/images/folder2',false,/\.(png|jpe?g|svg)$/));
return (
<div className="portfolio-item">
<div className="desc">
<div className="item-name">
<p>{project.name}</p>
</div>
<div className="image-block">
<div className="item-images">
<Carousel showArrows={true} showThumbs={false} >
// attempt 1 (this works fine, but it's hurdcoded ... so it's not a solution )
{project.image === 'folder1' ? folder1.map((image, index) => {
return (
<div className="image-block-small" key={index}>
<div>
<img src={image} alt=""/>
</div>
</div>
)})
: null}
</Carousel>
</div>
// attempt 2 (Here project.image equal folder1 (folder name), but here I get error "TypeError: webpack_require(...).context is not a function", because the arguments passed to require.context must be literals!)
{importAll(require.context('../../assets/images/' + project.image, false, /\.(png|jpe?g|svg)$/)).map((image, index) => {
return (
<div className="image-block-small" key={index}>
<div>
<img src={image} alt=""/>
</div>
</div>
)})
: null}
</Carousel>
</div>
I tried even to make it like a variable
const folder1 = '../../assets/images/folder1';
{importAll(require.context(folder1, false, /\.(png|jpe?g|svg)$/)).map((image, index) => { ... }
but got the same response as was in the second attempt.
Any help will be appreciated.
Unfortunately, I didn't get any answers to my question. So I've solved it using switch and extra component.
Images component
function importAll(r) {
return r.keys().map(r);
}
const folder1 = importAll(require.context('../../assets/images/folder1', false, /\.(png|jpe?g|svg)$/));
const folder2 = importAll(require.context('../../assets/images/folder2', false, /\.(png|jpe?g|svg)$/));
const folder3 = importAll(require.context('../../assets/images/folder3', false, /\.(png|jpe?g|svg)$/));
export default class Images extends Component {
selectFolder(param) {
switch(param) {
case 'folder1':
return folder1;
case 'folder2':
return folder2;
// etc ....
default:
return folder3 ;
}
}
render() {
return (
<div className="item-images">
<Carousel showArrows={true} showThumbs={false} >
{this.selectFolder(this.props.param).map((image, index) => {
return (
<div className="image-block-small" key={index}>
<img src={image} alt=""/>
</div>
)
})
}
</Carousel>
</div>
)
}
}
Add put it with props "image" that equal to "folder name" inside Child component
<div className="image-block">
<ProjectImages param={project.image}/>
</div>
Hope it will help someone else.
When I click on a card, the loadAboutInfo function works through which I transfer data to another component and display it there. But if I click again on the same card, then it is duplicated. How can I fix it?I have check which take card id and then if it the same it render but I click again it render one more card, but i need if it already exist than new card mustn't render
loadAboutInfo=(pokemonValue,pockemonImg,pokemonId)=>{
this.setState(prevState => ({
pokemonValue:[...prevState.pokemonValue, pokemonValue],
pockemonImg,
pokemonId
}))
}
render() {
return (
<div className="wrapper">
<div className="pokemonlist__inner__cards">
<div className="pokemonlist__cards">
{this.state.pokemonList.map((value,index)=>{
let pokemonImgTemplate = this.state.pokemonImgTemplate;
let pokemonId = value.id;
let pockemonImg = pokemonImgTemplate.replace('{id}',pokemonId);
return(
<div className="pokemonListCard" key={index} onClick={()=>this.loadAboutInfo(value,pockemonImg,pokemonId)}>
<PokemonCard
pockemonImg={pockemonImg}
pokemonName={value.name}
pokemonTypes={value.types}
/>
</div>
)
})}
</div>
<PokemonLoadMore
loadMore={this.loadMore}
currentPage={this.state.currentPage}
/>
</div>
</div>
);
}
}
component where i map get data
render() {
return (
<div className="pokemon__about">
{this.props.pokemonValue.map((value,index)=>{
let totalMoves = value.moves.length;
return(
<div className="pokemon__about__wrapper" key={index}>
{this.props.pokemonId == value.id ?
<div className="pokemon__about__inner" key={index}>
<AboutImage
pockemonImg={this.props.pockemonImg}
/>
<AboutName
pockemonName={value.name}
/>
<div className="pokemon__about__table">
<AboutPokemonTypes
pokemonTypes={value.types}
/>
<table>
<AboutPokemonWeight
pockemonWeight={value.weight}
/>
<AboutPokemonMoves
totalMoves={totalMoves}
/>
</table>
</div>
</div>
:
null
}
</div>
)
})}
</div>
);
On the loadAboutInfo you can check if there is already a pokemon with the same id on pokemonValue array, something like this:
loadAboutInfo = (pokemonValue,pockemonImg,pokemonId) => {
// this will get the first element that matches the id
const exists = this.state.pokemonValue.find(pokemon => pokemon.id === pokemonId)
if (!exists) {
this.setState(prevState => ({
pokemonValue:[...prevState.pokemonValue, pokemonValue],
pockemonImg,
pokemonId
}))
}
}
So it will update the state only if the clicked pokemon isn't in the pokemonValue array
I built a site with nextjs (with server express & API) and Reactjs.
I would like to create dynamic paginations because there is far too much result for statically generated, So I added server endpoint /publiations/page/:id, I put a getInitialsProps for keep the id in query
But actually, when I click on my main page /publications where my store is not empty to go to the next page (publications/page/1), the page reloads and the store is empty. How I can keep my store when I change route?
And here my publications/page/[id].js
const PublicationsPage = ({id}) => {
return (
<>
<MainMenu/>
<Search/>
<div className="flex">
<Sidebar fallback={<Loader/>}/>
<Cards type={'publications'} idPage={id} />
</div>
</>
)
}
PublicationsPage.getInitialProps = async function({ query: { id } }) {
return {
id: id
};
};
export default withAuthSync(PublicationsPage);
The cards components where i use the data of store :
components/Cards.js
const Cards = ({ idPage, cards, type }) => {
console.log(cards)
return (
<div className="cards">
<div className="content-filter-search">
<div className="content-newsearchresult">
<div className="global-name">Result: {cards.cards.length} articles found</div>
<div className="content-button-blue">
<Link href="/explorer">
<a>
<div className="button-blue">New search</div>
</a>
</Link>
</div>
</div>
<div className="content-filter">
{filters[idPage].map((item) => {
return <InputFilter key={item.id} data={item} callback={keepItems} />;
})}
</div>
</div>
<div className="wrapper-card">
<div className="cards-container">
{
!cards.loading ? cards.cards.slice(idPage * 9, idPage * 9 + 9).map((card) => (
<Card key={card.PMCID} data={card} />
)) : <Spinner color="black" size="100px" thickness={3} gap={5} speed="fast" />
}
</div>
</div>
<div className="text">
<Link href={'/publications/page/1'}><a>Next page</a></Link>
</div>
{
!cards.loading ? (
<div className="center">
<Pagination type={type} page={parseInt(idPage)} totalElement={cards.cards.length} numberPerPage={9} />
</div>
) : null
}
</div>
);
};
const mapStateToProps = state => ({
cards: state.cards,
})
export default connect(mapStateToProps)(Cards);
I use the same code for the route /publications and /publications/page/:id just I add the getInitialProps to keep the id of page. And I use connect redux in my Cards component
I have no error in the console just my store is reset because the page reload. I don't understand how I can make pagination with my store where is my data if when I change page the store is empty
Thanks