I'm building a pokedex website and I have pokemon cards with some data displayed in them from a JSON file and when you click on one of them you have a modal view that appears with more detailed data.
So in the modal view I want only the detailed data of the card I just clicked on it.
I have no idea how to do that if anyone can help, thanks.
This is Modal.tsx where I initialize my modal view, this where I want to get the pokemon name from Card.tsx (cf below) to be able to know which card was clicked :
import '../components/Modal.css';
import Data from '../pokemons.json';
import React from 'react';
export const Modal = ({showModal} : {showModal: boolean}) => {
return (
<>{showModal ? (
<div className="modal-back">
<div className="modal-container">
MODAL VIEW
</div>
</div>
): null}</>
);
};
This is Card.tsx where I handle the cards and where I call the modal view :
import Data from "../pokemons.json"
import '../components/Card.css'
import {FiThumbsUp} from "react-icons/fi"
import {useState} from 'react';
import {Modal} from './Modal';
function Card() {
const [showModal, setShowModal] = useState(false);
return(
<div className="cards">
{Data.map(card => {
return(
<div className="card-box" onClick={() => setShowModal(true)}>
<img src={card.img} alt="" />
<div className="text">
<div className="first-line">
<p className="id">{card.id}</p>
<p>{card.name}</p>
</div>
<div className="type-container">
{card.type.map((type, index) => {
return(
<div className="type" key={index}>
<p className={type}>{type}</p>
</div>
);
}) }
</div>
</div>
<div className="icon-circle">
<FiThumbsUp className="icon" color="#e5e5e5" size="18px"/>
</div>
</div>
);
}) }
<Modal showModal={showModal}></Modal>
</div>
);
}
export default Card;
You can pass selected card data as prop in the modal. You also need to update prop type as it only accepts one parameter.
Your Modal component will look like this:
interface ICard {
name: string,
...
}
interface props {
showModal: boolean;
card: ICard
}
export const Modal: FC<props> = ({showModal, card}) => {
return (
<>{showModal ? (
<div className="modal-back">
<div className="modal-container">
MODAL VIEW
</div>
<p>{card.name}</p>
</div>
): null}</>
);
};
You also need to update Card component to pass props. Make sure you're storing selected card data.
<Modal showModal={showModal} card={card} />
Related
I want to make it so when you click on a div it redirects you to another page, like react router but I have no knowledge to make it yet. Here is my code:
const Card: React.FC = ({ info }: any) => {
return (
<div className='card stacked featured'>
<img src={info.links.mission_patch} className='card_image' alt='NO-IMAGE'/>
<div className='card_content'>
<h2 className="card_title">{info.mission_name}</h2>
<p className='card_number'>Flight: {info.flight_number}</p>
<p className='card_description'>{info.details}</p>
</div>
</div>
)
}
Basically this is card, the data is from a web api. I want to make it so when I click on a card a whole new page shows with only that card data not other cards because they are iterated.
I suggest using useNavigate from react-router-dom. It is what I use for such things.
import { useNavigate } from 'react-router-dom'
const Card: React.FC = ({info}: any) => {
const navigate = useNavigate()
return (
<div className='card stacked featured'>
<img src={info.links.mission_patch} className='card_image' alt='NO-IMAGE'/>
<div className='card_content' onClick={() => navigate("/toThePageYouWantToNavigateTo")>
<h2 className="card_title">{info.mission_name}</h2>
<p className='card_number'>Flight: {info.flight_number}</p>
<p className='card_description'>{info.details}</p>
</div>
</div>
)
}
Import and render the Link component from react-router-dom.
import { Link } from 'react-router-dom';
...
const Card: React.FC = ({ info }: any) => {
return (
<div className='card stacked featured'>
<img src={info.links.mission_patch} className='card_image' alt='NO-IMAGE'/>
<Link
className='card_content'
to={`"/mission/${info.id}`} // <-- this is path you want to link to
>
<h2 className="card_title">{info.mission_name}</h2>
<p className='card_number'>Flight: {info.flight_number}</p>
<p className='card_description'>{info.details}</p>
</Link>
</div>
);
};
If you don't want to render an actual link/anchor tag into the DOM then import and use the useNavigate hook and add an onClick handler to the div element.
import { Link } from 'react-router-dom';
...
const Card: React.FC = ({ info }: any) => {
const navigate = useNavigate();
return (
<div className='card stacked featured'>
<img src={info.links.mission_patch} className='card_image' alt='NO-IMAGE'/>
<div
className='card_content'
onClick={() => navigate(`"/mission/${info.id}`)} // <-- this is path you want to link to
>
<h2 className="card_title">{info.mission_name}</h2>
<p className='card_number'>Flight: {info.flight_number}</p>
<p className='card_description'>{info.details}</p>
</div>
</div>
);
};
let me explain my situation.
I am building a MERN project to my portfolio and I am trying to make a button toggle between the name of an item and a inputfield. So when the user click the pen (edit), it will add a class with the displain:none; in the div with the text coming from the MongoDB data base to hide it and will remove it from the div with the input. I could manage to do it. BUT since the amount of items can inscrease, clicking in one of them cause the toggle in all of them.
It was ok until I send some useState as props to the component.
This is my code from the App.jsx
import React, {useState, useEffect} from "react";
import Axios from "axios";
import "./App.css";
import ListItem from "./components/ListItem";
function App() {
//here are the use states
const [foodName, setFoodName] = useState("");
const [days, setDays] = useState(0);
const [newFoodName, setNewFoodName] = useState("");
const [foodList, setFoodList] = useState([]);
//here is just the compunication with the DB of a form that I have above those components
useEffect(() => {
Axios.get("http://localhost:3001/read").then((response) => {
setFoodList(response.data);
});
}, []);
const addToList = () => {
Axios.post("http://localhost:3001/insert", {
foodName: foodName,
days: days,
});
};
const updateFood = (id) => {
Axios.put("http://localhost:3001/update", {
id: id,
newFoodName: newFoodName,
});
};
return (
<div className="App">
//Here it starts the app with the form and everything
<h1>CRUD app with MERN</h1>
<div className="container">
<h3 className="container__title">Favorite Food Database</h3>
<label>Food name:</label>
<input
type="text"
onChange={(event) => {
setFoodName(event.target.value);
}}
/>
<label>Days since you ate it:</label>
<input
type="number"
onChange={(event) => {
setDays(event.target.value);
}}
/>
<button onClick={addToList}>Add to list</button>
</div>
//Here the form finishes and now it starts the components I showed in the images.
<div className="listContainer">
<hr />
<h3 className="listContainer__title">Food List</h3>
{foodList.map((val, key) => {
return (
//This is the component and its props
<ListItem
val={val}
key={key}
functionUpdateFood={updateFood(val._id)}
newFoodName={newFoodName}
setNewFoodName={setNewFoodName}
/>
);
})}
</div>
</div>
);
}
export default App;
Now the component code:
import React from "react";
//Material UI Icon imports
import CancelIcon from "#mui/icons-material/Cancel";
import EditIcon from "#mui/icons-material/Edit";
//import CheckIcon from "#mui/icons-material/Check";
import CheckCircleIcon from "#mui/icons-material/CheckCircle";
//App starts here, I destructured the props
function ListItem({val, key, functionUpdateFood, newFoodName, setNewFoodName}) {
//const [foodList, setFoodList] = useState([]);
//Here I have the handleToggle function that will be used ahead.
const handleToggle = () => {
setNewFoodName(!newFoodName);
};
return (
<div
className="foodList__item"
key={key}>
<div className="foodList__item-group">
<h3
//As you can see, I toggle the classes with this conditional statement
//I use the same classes for all items I want to toggle with one click
//Here it will toggle the Food Name
className={
newFoodName
? "foodList__item-newName-delete"
: "foodList__name"
}>
{val.foodName}
</h3>
<div
className={
newFoodName
? "foodList__item-newName-group"
: "foodList__item-newName-delete"
}>
//Here is the input that will replace the FoodName
<input
type="text"
placeholder="The new food name..."
className="foodList__item-newName"
onChange={(event) => {
setNewFoodName(event.target.value);
}}
/>
//Here it will confirm the update and toggle back
//Didn't implement this yet
<div className="foodList__icons-confirm-group">
<CheckCircleIcon
className="foodList__icons-confirm"
onClick={functionUpdateFood}
/>
<small>Update?</small>
</div>
</div>
</div>
//here it will also desappear on the same toggle
<p
className={
newFoodName
? "foodList__item-newName-delete"
: "foodList__day"
}>
{val.daysSinceIAte} day(s) ago
</p>
<div
className={
newFoodName
? "foodList__item-newName-delete"
: "foodList__icons"
}>
//Here it will update, and it's the button that toggles
<EditIcon
className="foodList__icons-edit"
onClick={handleToggle}
/>
<CancelIcon className="foodList__icons-delete" />
</div>
</div>
);
}
export default ListItem;
I saw a solution that used different id's for each component. But this is dynamic, so if I have 1000 items on the data base, it would display all of them, so I can't add all this id's.
I am sorry for the very long explanation. It seems simple, but since I am starting, I spent the day on it + searched and tested several ways.
:|
So I'm pretty much new in React/Web development and just can't figure it out regarding ReactPlayer.
I have a .JSON file with [ID, Question, URL] and I load the questions into divs. What I want is when I click the div(question) then the URL that is assigned to that question should load in the ReactPlayer..
This is how it looks so far:
import React, { useState } from "react";
import Questions from "../data/questions.json";
import style from "./Card.module.css";
import ReactPlayer from "react-player/youtube";
function Card() {
const handleClick = (item) => {
console.log(item);
};
return (
<div>
<div className={style.ViewContent}>
<div className={style.mainCard}>
{ListQuestions.map((ListItem, index) => {
return (
<div onClick={() => handleClick(ListItem.url)} key={index} className={style.Card}>
<h3 className={style.Titel}>{ListItem.question}</h3>
</div>
);
})}
</div>
<div className={style.VideoPlayer}>
<ReactPlayer url={handleClick.item} controls={true} />
</div>
</div>
</div>
);
}
export default Card;
I tested the click function and every time I click the question the console logs only the URL.
But how can the ReactPlayer get that URL and play the video?
I'm sorry for the bad coding.. still learning :)
I tried adding onSubmit on the div box so when clicking the div it should submit/load the link to the ReactPlayer... but thinking logically and then interpreting it kind of does not work.
I figured it out :D
import React, { useState } from "react";
import Questions from "../data/questions.json";
import style from "./Card.module.css";
import ReactPlayer from "react-player";
function Card() {
const [playUrl, setPlayUrl] = useState(""); ← here you could put the youtube link to show up when loading the page.
const [isPlaying, setIsPlaying] = useState(true);
return (
<div>
<div className={style.ViewContent}>
<div className={style.mainCard}>
{ListQuestions.map((ListItem, index) => {
return (
<div onClick={() => setPlayUrl(ListItem.url)} key={index} className={style.Card}>
<h3 className={style.Titel}>{ListItem.question}</h3>
</div>
);
})}
</div>
<div className={style.VideoPlayer}>
<ReactPlayer url={playUrl} controls={true} playing={isPlaying} />
</div>
</div>
</div>
);
}
export default Card;
I am developing a web app with reactjs to make the supermarket list.
Each product comes from the project database that is in firestore (google tool) and they are all shown by means of a .map() with their specific image and name.
The idea is to each product the user can add a description or data that they want to remember for their future purchase, this description is entered by the user from a modal component that has a where the user can write any description about the product. The Modal component is inside the productItemCard component.
In part the localStorage is working fine since this description is not deleted when updating the browser, but the problem is that the description that the user entered for a product is added to all other products. Both those who are on the list and those who are not. The idea is that each product has its own description, that the user can check what he wrote about each one, and that if he does not add a description, the empty textarea will simply appear with its respective placeholder.
https://github.com/franciscominen/supermarket-list-app
// MODAL WITH TEXTAREA COMPONENT
import React, {useContext, useState, useEffect} from 'react';
import "./styles.scss"
import Popup from 'reactjs-popup';
import { toast, Slide } from 'react-toastify';
import {listContext} from '../../utils/ListContext';
const ModalComponent = ({item}) => {
// ADD ITEM AND CONTEXT
const {addItem, note, noteChange} = useContext(listContext);
const onAdd = () => {
addItem(item)
}
// TOAST NOTIFY ITEM ADD
const notify = () => toast( `Se agregó ${item.name} a su lista.`, {
position: "bottom-center",
autoClose: 1500,
hideProgressBar: true,
closeOnClick: false,
pauseOnHover: true,
draggable: true,
progress: undefined,
transition: Slide
});
return (
<Popup
trigger={<button className="button"> <FiEdit style={{color:'#242424', fontSize:'20px'}}/>
</button>}
modal
nested
>
{close => (
<Animated animationIn="fadeIn" animationOut="zoomOut" animationInDuration={300}
animationOutDuration={300}>
<div className="modal">
<div className="header">
<div className='item'>
<img src={item.img} alt=""/>
<h2>{item.name} </h2>
</div>
<button className="close" onClick={close}>
<IoCloseOutline />
</button>
</div>
<div className="content">
// TEXT AREA
<textarea
placeholder={window.location.href === "http://localhost:3000/productos"
? "Agregue aqui una descripcion sobre este producto"
: note }
value={note}
onChange={noteChange}
/>
</div>
<div className='modal_footer' onClick={notify}>
{ window.location.href === "http://localhost:3000/productos" // MODIFICAR AL HOSTEAR
? <button onClick={onAdd}>Agregar a mi lista <BsListCheck/> </button>
: <button disabled style={{display:'none'}}>Agregar a mi lista <BsListCheck/> </button>
}
</div>
</div>
</Animated>
)}
</Popup>
);
}
export default ModalComponent;
//
// MAP PRODUCTS COMPONENT
import React, {useContext} from 'react';
import {ProductCard} from '../ProductCardComponent/ProductCard';
import {listContext} from "../../utils/ListContext";
export const ProductList = ({ items }) => {
const {searchTerm} = useContext(listContext)
return (
<>
{ items.filter( item => { // SEARCH
if (searchTerm == "") {
return item
} else if (item.name.toLowerCase().includes(searchTerm.toLowerCase())) {
return item
}
}).map( item => (
<ProductCard
key={item.id}
item={item}
/>
))}
</>
)
};
//
//
PRODCUT CARD COMPONENT
import React, {useContext} from 'react';
import "./styles.scss"
import {RiAddFill} from "react-icons/ri";
import {listContext} from "../../utils/ListContext";
import { toast, Slide } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import {Animated} from "react-animated-css";
import ModalComponent from './DescripcionModal';
export const ProductCard = ({ item }) => {
const {addItem} = useContext(listContext)
const notify = () => toast( `Se agregó ${item.name} a su lista.`, {
position: "bottom-center",
autoClose: 1500,
hideProgressBar: true,
closeOnClick: false,
pauseOnHover: true,
draggable: true,
progress: undefined,
transition: Slide
});
const onAdd = () => {
addItem(item)
}
return (
<>
<Animated animationIn="zoomIn" animationOut="fadeOut" isVisible={true} animationInDuration={500} animationInDuration={500} >
<div className='card_product' >
<div className='btns_container'>
<ModalComponent item={item} />
<button onClick={onAdd}>
<RiAddFill onClick={notify}/>
</button>
</div>
<img src={item.img} onerror="this.src='https://ctkbiotech.com/wp/wp-content/uploads/2018/03/not-available.jpg'"/>
<h1>{item.name}</h1>
</div>
</Animated>
</>
)
};
// ITEMS IN USER LIST COMPONENT
import React, {useContext} from 'react';
import './styles.scss';
import {listContext} from "../../utils/ListContext";
import {IoCloseOutline} from "react-icons/io5";
import ModalComponent from "../ProductCardComponent/DescripcionModal"
const ItemsInList = () => {
const { list, removeItem, note } = useContext(listContext);
return (
<>
<div style={{display:'flex', flexDirection:'column', margin:'20px 0'}}>
{list.map(({item}) => {
return (
<>
<section className='listProducts_container'>
<div className='list_product' id='listProduct'>
<div className='item_detail'>
<img src={item.img} />
<div className='descipt_container'>
<h2>{item.name}</h2>
<p style={{color:'grey', fontSize:'14px'}}>
{note}
</p>
</div>
</div>
<div style={{display:'flex'}}>
<ModalComponent item={item}/>
<button onClick={()=>{removeItem(item)}}>
<IoCloseOutline style={{marginLeft:'10px'}}/>
</button>
</div>
</div>
</section>
</>
)
})}
</div>
</>
)
}
export default ItemsInList;
I think your problem is here
<p style={{color:'grey', fontSize:'14px'}}>
{note}
</p>
This note is a singular variable, not one tied to a specific item. I think what you are looking to do is store the note in the item so, you want to update the item's note and display that same note:
<p style={{color:'grey', fontSize:'14px'}}>
{item.note}
</p>
I have a Cards component which takes in the props from the UserPostscomponent(which is connected to the store) and displays cards. Cards is not connected to the redux store and I want to dispatch an action in the handleDelete function. How can I do that?
import React, { Component } from "react"
class Cards extends Component {
handleDelete = (id) => {
}
render() {
const { title, description } = this.props.post
const { postId } = this.props.post._id
return (
<div className="card">
<div className="card-content">
<div className="media">
<div className="media-left">
<figure className="image is-48x48">
<img
src="https://bulma.io/images/placeholders/96x96.png"
alt="Placeholder image"
/>
</figure>
</div>
<div className="media-content" style={{border: "1px grey"}}>
<p className="title is-5">{title}</p>
<p className="content">{description}</p>
<button className="button is-success">Edit</button>
<button onClick={this.handleDelete(postId)} className="button is-success">Delete</button>
</div>
</div>
</div>
</div>
)
}
}
export default Cards
UserPosts component which passes the props
<div>
{userPosts &&
userPosts.map(post => {
return <Cards key={post._id} post={post} />
})}
</div>
```
You can use the global store and directly call dispatch method. Not recommended. Hard to maintain and debug.
import { createStore } from 'redux'
const store = createStore(todos, ['Use Redux'])
// Dont create new one, use the one created in root
function addTodo(text) {
return {
type: 'ADD_TODO',
text
}
}
store.dispatch(addTodo('Read the docs'))
store.dispatch(addTodo('Read about the middleware'))