I asked a similar question (ReactJs/JS/TailWindCSS - Infinite Image Slider), but this only solved the problem of making an infinite image slider, but I need something that is dynamic, here is the modified code from the question,
import {db} from "./../firebase";
import React, {useEffect, useState} from "react";
import { collection, onSnapshot } from "firebase/firestore";
export default function scrollImagesRight() {
const [images, setImages] = useState([]);
useEffect(()=>{
onSnapshot(collection(db, "sponsors"), (snapshot) =>{
const data: React.SetStateAction<[]> = [];
snapshot.forEach((doc) => {
data.push(doc.data());
});
setImages(data);
});
},[])
return(
<div className="w-full bg-red-100 flex justify-center">
<div className="h-[200px] m-auto overflow-hidden relative w-auto">
<ul className={"flex w-[calc(250px*",images.length,")] animate-scroll"}>
{images.map((ref, index)=>{
return(
<li key={index} className="w-[250px]">
<img src={ref.image} alt={ref.name} />
</li>
);
})}
</ul>
</div>
</div>
);
};
How can I make it so that it automatically keeps working with any number of images?
Related
This question already has answers here:
React setState not updating state
(11 answers)
The useState set method is not reflecting a change immediately
(15 answers)
Closed 17 days ago.
This post was edited and submitted for review 17 days ago and failed to reopen the post:
Original close reason(s) were not resolved
I make an ecommerce site I have a page that displays all the products and on each product I put a link tag that refers to my page where I will put the product details with its id like this :
<div className="flex -mx-5 overflow-x-scroll snap-x scrollbar-hide">
{products.filter(p => p.category === categoryName).map(productInfo => (
<Link href={`/product?id=${productInfo._id}`} key={productInfo._id} className="px-5 snap-start border rounded-xl shadow-md m-2 p-3 bg-white">
<Product {...productInfo}/>
</Link>
))}
</div>
and my page with product details where I get the id from the url :
import React, { useState, useEffect } from "react";
import Link from "next/link";
import { useRouter } from "next/router";
import axios from "axios";
import Layout from "../components/Layout";
const ProductPage = () => {
const router = useRouter();
const [product, setProduct] = useState({});
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchProduct = async () => {
try {
const response = await axios.get(`/api/products?id=${router.query.id}`);
setLoading(false);
setProduct(response.data);
console.log(product)
} catch (error) {
console.error(error);
}
};
fetchProduct();
}, [router.query.id]);
console.log(product)
return (
<Layout title={product.name}>
{loading ? (
<div className="text-center">Loading...</div>
) : (
<div className="max-w-lg mx-auto p-5">
<h1 className="text-2xl font-bold mb-5">{product.name}</h1>
<img
src={product.imageUrl}
alt={product.name}
className="w-full mb-5"
/>
<p className="mb-5">{product.description}</p>
<p className="text-xl font-bold mb-5">
Price: {product.price} {product.currency}
</p>
<Link href="/" legacyBehavior>
<a className="btn btn-primary">Go back to the products list</a>
</Link>
</div>
)}
</Layout>
);
};
export default ProductPage;
The values are not displayed so I put some console.log
When I put a console.log in the "try" product returns an empty object, and when I put the console.log after the useffect the product returns an object with my values
I am sick of defining states to render Components on condition. Most of the time I just need to show some kind of notification or alert. I want to know how can I render a component by calling a function from that Component.
I have found some sample code that is doing exactly what I want, but I can´t reverse engineer it to implement this on my own as I have no clue how the Modal.info() function is adding itself to the DOM.
I want to recreate the Modal Component for myself and display it by calling MyModal.info().
import { Modal, Button, Space } from 'antd';
const Item = (props: ItemProps) => {
const { itemGroup, items } = props;
function info() {
Modal.info({
title: 'This is a notification message',
content: (
<div>
<p>some messages...some messages...</p>
<p>some messages...some messages...</p>
</div>
),
onOk() {},
});
}
return (
<div className="py-6">
<div
onClick={() => info()}
className="cursor-pointer py-6 px-6 text-3xl font-heading font-bold bg-primary text-white"
>
<p>{itemGroup.text}</p>
</div>
<div className={`${isOpen ? 'block' : 'hidden'} duration-200 transition-all p-3 bg-gray-200`}>
<ul className="grid grid-cols-1 md:grid-cols-2 gap-6">
{items.map((x) => (
<ItemCard key={x.id} itemData={x} />
))}
</ul>
</div>
</div>
);
};
I came up with following solution.
Notification.tsx
import React from 'react';
import ReactDOM from 'react-dom';
export class Notifier {
static warn() {
if (!document) return;
const rootElement = document.getElementById('__next');
ReactDOM.render(React.createElement(Notification), rootElement);
}
}
const Notification = () => {
return (
<div className="w-full h-full bg-red-500 absolute top-0 left-0">Notification Content</div>
);
};
With this solution I can insert anywhere I want my Modal by calling Notifier.warn().
The only thing I am insecure about is the bundle size from ReactDOM which actually ads 125Kb to Notification.tsx.
You should be able to call Modal.info() like this
<Modal.info
title='This is a notification message',
content={) => (
<div>
<p>some messages...some messages...</p>
<p>some messages...some messages...</p>
</div>
)}
onOk={() => {}}
/>;
All functional components can be called like components.
If this doesn't work then Modal.info is not a component.
To trigger it you should follow the first example from the docs.
https://ant.design/components/modal/
You need to manage some sort of state to tell the Modal to open, functional components can manage something like state using hooks.
Like here
https://reactjs.org/docs/hooks-state.html
For custom, you will need to create your own modal design, likely in a react portal, design as you want. But opening/closing will be handled through useState hooks.
import React, { useState } from 'react';
const Component = props => {
const [open, setOpen] = useState(false);
return (
<>
<Button onClick={() => setOpen(true)}>Open</Button>
{open && <Modal>
<Button onClick={() => setOpen(false)}>Close</Button>
</Modal> }
</>
)
}
I'm trying to render a blog as a card then open it up as a page , but its proving to be difficult using Gatsby. I did the same thing fine with react using React router and useLocation but it doesn't seem to be working with Gatsby.
I switched to reach router as suggested in another post but that doesnt work. Im looking for another method now that perhaps does not need to use useLocation.
I kept getting this error when I used react-router-dom:
Invariant failed: You should not use <Link> outside a <Router>
function Blog() {
const [blogs, setBlogs] = useState([])
const [image, setImage] = useState()
const [selectedBlog, setSelectedBlog] = useState(blogs)
useEffect(() => {
fetch("http://cdn.contentful.com...")
.then(response => response.json())
.then(data =>
setBlogs(data.items)
)
}, [])
console.log(blogs)
return (
<>
<div className="card-flex" >
{selectedBlog !== null ? blogs.map((blog =>
<Card title={blog.fields.title} date={blog.fields.date} introduction={blog.fields.introduction} mainBody1={blog.fields.mainBody1} mainBody2={blog.fields.mainBody2} setSelectedBlog={selectedBlog}
/>
)):
<Article title={blogs.find(d => d.fields.title === selectedBlog)} />
}
</div>
</>
)
}
export default Blog
Blog Card
function Card(props) {
console.log(props)
return (
<div class="container">
<div class="card">
<div class="card-header">
<img style={{backgroundImage: "url('https://i.pinimg.com/564x/7f/bb/97/7fbb9793b574c32f5d28cae0ea5c557f.jpg')"}}/>
</div>
<div class="card-body">
<span class="tag tag-teal">{props.tags}</span>
<h4>{props.title}</h4>
<p style={{fontSize:"17px", paddingTop:"10px"}} >{props.introduction}</p>
<div class="card-user">
<Link
to={{
pathname: '/article',
state: {
title: props.title,
introduction: props.introduction
}
}}
>
<button>read more</button>
</Link>
<div class="user-info">
<h5 >{ props.date}</h5>
</div>
</div>
</div>
</div>
</div>
)
}
export default Card
Article Page
import React from 'react'
import './Article.css'
import { useLocation } from "#reach/router"
function Article(props) {
// useLocation to access the route state from Blog.js
const { state = {} } = useLocation();
console.log(state)
return (
<div className="main">
<h1 className="title">{state.title}</h1>
<p className="intro">{state.introduction}</p>
<p className="main1">{state.mainBody1}</p>
<p className="main2">{state.mainBody2}</p>
</div>
)
}
export default Article
I believe you're not supposed to use react-router on a Gatsby project: https://www.gatsbyjs.com/docs/reference/routing/creating-routes/
For a normal project you could do:
Go to your top-most element and wrap it with a Router. https://reactrouter.com/web/api/BrowserRouter
You basically have to search for ReactDom.render(<YourApp />) and do ReactDom.render(<Router><YourApp /></Router>)
To practice ReactJS, I'm trying to develop a small application. I am unable to solve the key-error.
What I'm trying to do:
The Pokemon can be added to a collection which is created by the user. I created 2 components for this. One component renders all the Pokemon cards and the other component is a Pokemon card.
The components are as follows -
PokemonCards.js
/** #jsx jsx */
import { jsx, css } from "#emotion/core"
import tw from "twin.macro"
import PokemonCard from "./PokemonCard"
import { useEffect } from "react"
import { useDispatch, useSelector } from "react-redux"
import { fetchPokemonNameUrl, selectorPokemon } from "./pokemonCardsSlice"
const PokemonCards = () => {
const dispatch = useDispatch()
const pokemonList = useSelector(selectorPokemon)
useEffect(() => {
dispatch(fetchPokemonNameUrl())
}, [dispatch])
return (
<div tw="p-2">
Pokemon Cards
<section tw="grid grid-cols-1 gap-2">
<ul>
{pokemonList.map(poke => (
<PokemonCard
key={`key-${poke.id}`}
pokemonId={poke.id}
pokemonName={poke.name}
pokemonType={poke.type}
pokemonHeight={poke.height}
pokemonWeight={poke.weight}
pokemonBaseExperience={poke.baseExperience}
pokemonSprite={poke.sprites}
/>
))}
</ul>
</section>
</div>
)
}
export default PokemonCards
PokemonCard.js
/** #jsx jsx */
import { jsx, css } from "#emotion/core"
import tw from "twin.macro"
import { useState } from "react"
import { useSelector, useDispatch } from "react-redux"
import { add, selectorCollection } from "../home/collectionSlice"
const PokemonCard = props => {
const collection = useSelector(selectorCollection)
const dispatch = useDispatch()
const [collectionInput, setCollectionInput] = useState("")
const addPokemonToList = e => {
e.preventDefault()
console.log(collectionInput)
}
const {
pokemonId,
pokemonName,
pokemonType,
pokemonHeight,
pokemonWeight,
pokemonBaseExperience,
pokemonSprite,
} = props
return (
<div tw="flex flex-row justify-around items-center bg-red-500 p-2 my-2 rounded">
<div tw="">
<img tw="bg-cover bg-center" alt={pokemonName} src={pokemonSprite} />
</div>
<div tw="mx-1">
<p>{pokemonName}</p>
<p>{pokemonType}</p>
<p>{pokemonHeight}</p>
<p>{pokemonWeight}</p>
<p>{pokemonBaseExperience}</p>
</div>
<div tw="mx-1">
<form onSubmit={addPokemonToList}>
<label>
Add Pokemon to collection <br />
<select
value={collectionInput}
onChange={e => setCollectionInput(e.target.value)}
>
{collection.map(col => (
<option key={`${pokemonId}-${col.name}`} value={col.name}>
{col.name}
</option>
))}
</select>
</label>
<button type="submit" tw="bg-gray-300 p-1 rounded">
add
</button>
</form>
</div>
</div>
)
}
export default PokemonCard
Here's what my state looks like:
I've put unique keys for both:
Pokemon cards
<option> in the <select> input which is present inside each card.
If I comment out the <form> in PokemonCard.js, the warning goes away. Which means, the error lies in the creation of <options> for the <select> input using map().
Here's the Github repo.
I checked your Github repo. Here's the problem
export const pokemonCardsSlice = createSlice({
name: "pokemonCards",
initialState: initialState,
reducers: {
getData: (state, action) => {
state.pokemonList.push(action.payload)
},
},
})
Here in reducer you push to the store. That's okay but you fetch data in a child component call PokemonCards.js. So every time this component re-render, your dispatcher called with the action and push pokemonList to the end of the store.
To solve the issue, you can either fetch pokemon list in App component, or you can filter payload in reducer and only push if it is not in the store
In react, key should be unique. So you can add an index with it to make it unique.
{
collection.map((col,index) => (
<option key={`${pokemonId}-${col.name}-${index}`} value={col.name}>
{col.name}
</option>
))
}
If you have a key id in collection, then you can use it as a key of your options:
{collection.map(col => (
<option key={`${col.id}-${col.name}`} value={col.name}>
{col.name}
</option>
))}
For a better way, use destructuring
{collection.map(({id, name}) => (
<option key={`${id}-${name}`} value={name}>
{col.name}
</option>
))}
I have product page with a row of shoe brand logos like Nike, Adidas, etc..and below the row are images of all our shoe inventory. When the user clicks on a brand, for example nike, the url changes to /nike and filters the images to show just nike shoes. Then if I click Adidas, url changes to /adidas and filters to show only adidas shoes. However, when I the back button on the browser, the URL will change back to /nike but the images remain adidas. I have useEffect to run on any change to the brandId which is the prop that is used to filter the shoes.
Here's my code:
import React, { useState, useEffect} from 'react'
import "../styles/ShoesPage.css"
import "../styles/Players.css"
import { Link } from 'react-router-dom'
import Axios from 'axios'
export default props => {
const brandId = props.match.params.brand
const [shoes, setShoes] = useState([])
useEffect(() =>{
Axios.get(`/shoes/brands/+${brandId}`).then(res=>
setShoes(res.data)
)
}, [brandId])
return (
<>
<div id="playercontainer">
{shoes.map((shoe, i) => (
<Link to={"/product/" + shoe.id} key={'shoe' + i}>
<div className="player">
<img className="shoeImg" src={`${shoe.pic}`} alt="" />
<div className="playerDesc">
<div className="teamName">{shoe.brand}</div>
<div className="playerName">{shoe.shoe}</div>
<div className="shoePrice">${shoe.price}</div>
</div>
</div>
</Link>
))}
</div>
</>
)
}