React: Limit action to a single component - reactjs

This is a container:
render() {
return (
<div>
<ul className="list-group" id="contact-list">
{this.returnContactList().map(
(contact) =>
<li key={contact.date.N} className="list-group-item">
<ContactCard contact={contact} onFormSubmit={this.handleSubmit} summaryHidden={this.state.summaryHidden} />
</li>
)}
</ul>
</div>
);
}
This is what I have as a component:
import React from 'react';
import '../Contacts.css';
const ContactCard = ({ contact, onFormSubmit, summaryHidden }) => {
return (
<div>
<button onClick={onFormSubmit}>submit</button>
<div style={{ display: summaryHidden ? 'block' : 'none' }}>
Summary
</div>
</div>
)
}
export default ContactCard;
This renders list of contacts. But when I click button, the text Summary gets applied to all components. I want to limit it to a single component only. How to do that?

You only have one "summaryHidden" variable in your container. You need a separate one for each component (maybe store them on the contact objects in the map) if you want the summaries to be hidden in some components and not others.

Related

Add element in array by button click

I have product cards that are rendered based on a json file.
By clicking on the "Add to Cart" button, the element should be added to the array сartList, but this does not happen.
I also tried to forward the function to the component itself, but it didn’t work out too well for me.
Shop.jsx:
import React, { useState } from 'react';
import './Instruments.css';
import Cart from '../components/Cart'
import Product from '../components/Product'
import cart from '../img/cart.png';
import data from "../data/data.json";
unction Shop() {
const [value, setValue] = useState('');
const [currentData, setCurrentData] = useState(data);
const [cartList, setCartList] = useState([]);
return (
<div className='shop'>
<div className='container'>
<div className='shop__main-products'>
{
currentData.filter((el) => {
return value.toLowerCase() === '' ? el : el.title.toLowerCase().includes(value.toLowerCase())
}).map((el, index) => {
return (
<Product img={el.img} title={el.title} price={el.price} key={el.id} onClick={() => setCartList([...cartList, el])}/>
)
})
}
</div>
</div>
<Cart active={modalActive} setActive={modalSetActive}/>
</div>
</div>
);
}
export default Shop;
Product.jsx:
import React, { useState } from 'react';
import './Product.css';
function Product({img, title, price, id, type}) {
return (
<div className='product' key={id} type={type}>
<div className='buy__top'>
<div className='product__top-image-background'>
<img className='product__top-image' src={img}></img>
</div>
<h3 className='product__top-title'>{title}</h3>
</div>
<div className='product__buy'>
<h3 className='product__buy-price'>{price} грн</h3>
<button className='product__buy-button'>В корзину</button>
</div>
</div>
)
}
export default Product;
It looks like the issue is with how you're passing the onClick function to the Product component. The onClick prop should be passed to the "Add to Cart" button, not the Product component itself. You should change the following line:
<Product img={el.img} title={el.title} price={el.price} key={el.id} addToCart={() => setCartList([...cartList, el])}/>
And in the Product component, you should add the onClick prop to the "Add to Cart" button:
<button className='product__buy-button' onClick={addToCart}>В корзину</button>
This way, when the button is clicked, it will call the addToCart function and add the element to the cartList array.
You are not adding the onClick function to the props of the Product component pass it down the pipe and itll work.
function Product({img, title, price, id, type, onClick}) {
return (
<div className='product' key={id} type={type}>
<div className='buy__top'>
<div className='product__top-image-background'>
<img className='product__top-image' src={img}></img>
</div>
<h3 className='product__top-title'>{title}</h3>
</div>
<div className='product__buy'>
<h3 className='product__buy-price'>{price} грн</h3>
<button className='product__buy-button' onClick={onClick}>В корзину</button>
</div>
</div>
)
}

How to open dynamic modal with react js

I am trying to convert the HTML/Javascript modal to React js.
In Reactjs, I just want to open the modal whenever the user clicks the View Project button.
I have created a parent component (Portfolio Screen) and a child component (Portfolio Modal). The data I have given to the child component is working fine but the modal opens the first time only and then does not open. Another problem is that the data does not load even when the modal is opened the first time.
Codesandbox link is here.
https://codesandbox.io/s/reverent-leftpad-lh7dl?file=/src/App.js&resolutionWidth=683&resolutionHeight=675
I have also shared the React code below.
For HTML/JavaScript code, here is the question I have asked before.
How to populate data in a modal Popup using react js. Maybe with hooks
Parent Component
import React, { useState } from 'react';
import '../assets/css/portfolio.scss';
import PortfolioModal from '../components/PortfolioModal';
import portfolioItems from '../data/portfolio';
const PortfolioScreen = () => {
const [portfolio, setportfolio] = useState({ data: null, show: false });
const Item = (portfolioItem) => {
setportfolio({
data: portfolioItem,
show: true,
});
};
return (
<>
<section className='portfolio-section sec-padding'>
<div className='container'>
<div className='row'>
<div className='section-title'>
<h2>Recent Work</h2>
</div>
</div>
<div className='row'>
{portfolioItems.map((portfolioItem) => (
<div className='portfolio-item' key={portfolioItem._id}>
<div className='portfolio-item-thumbnail'>
<img src={portfolioItem.image} alt='portfolio item thumb' />
<h3 className='portfolio-item-title'>
{portfolioItem.title}
</h3>
<button
onClick={() => Item(portfolioItem)}
type='button'
className='btn view-project-btn'>
View Project
</button>
</div>
</div>
))}
<PortfolioModal portfolioData={portfolio} show={portfolio.show} />
</div>
</div>
</section>
</>
);
};
export default PortfolioScreen;
Child Component
import React, { useState, useEffect } from 'react';
import { NavLink } from 'react-router-dom';
const PortfolioModal = ({ portfolioData, show }) => {
const portfolioItem = portfolioData;
const [openModal, setopenModal] = useState({ showState: false });
useEffect(() => {
setopenModal({
showState: show,
});
}, [show]);
return (
<>
<div
className={`portfolio-popup ${
openModal.showState === true ? 'open' : ''
}`}>
<div className='pp-inner'>
<div className='pp-content'>
<div className='pp-header'>
<button
className='btn pp-close'
onClick={() =>
setopenModal({
showState: false,
})
}>
<i className='fas fa-times pp-close'></i>
</button>
<div className='pp-thumbnail'>
<img src={portfolioItem.image} alt={`${portfolioItem.title}`} />
</div>
<h3 className='portfolio-item-title'>{portfolioItem.title}</h3>
</div>
<div className='pp-body'>
<div className='portfolio-item-details'>
<div className='description'>
<p>{portfolioItem.description}</p>
</div>
<div className='general-info'>
<ul>
<li>
Created - <span>{portfolioItem.creatDate}</span>
</li>
<li>
Technology Used -
<span>{portfolioItem.technologyUsed}</span>
</li>
<li>
Role - <span>{portfolioItem.Role}</span>
</li>
<li>
View Live -
<span>
<NavLink to='#' target='_blank'>
{portfolioItem.domain}
</NavLink>
</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
};
export default PortfolioModal;
You don't have to use one useState hook to hold all your states. You can and I think you should break them up. In the PortfolioScreen component
const [data, setData] = useState(null);
const [show, setShow] = useState(false);
I changed the function Item that is used to set the active portfolio item to toggleItem and changed it's implementation
const toggleItem = (portfolioItem) => {
setData(portfolioItem);
setVisible(portfolioItem !== null);
};
You should use conditional rendering on the PortfolioModal, so you won't need to pass a show prop to it, and you'll pass a closeModal prop to close the PortfolioModal when clicked
{visible === true && data !== null && (
<PortfolioModal
data={data}
closeModal={() => toggleItem()} // Pass nothing here so the default value will be null and the modal reset
/>
)}
Then in the PortfolioModal component, you expect two props, data and a closeModal function
const PortfolioModal = ({ data, closeModal }) => {
And the close button can be like
<button className="btn pp-close" onClick={closeModal}>
...

React router - click on card and redirect to a new page with content of that card (card details)

I have a list of cards that shows movies/TV shows. What I need is when I click on a card, it redirects me to a new page (Details page) where I can see the details of that specific movie/card.
My card.js and card-collection.js are:
card :
import React from 'react';
import './card.styles.css';
const Card = ({ item }) => (
<div className='card-container'>
<div
className='image'
style={{
backgroundImage: `url(https://image.tmdb.org/t/p/w1280${item.backdrop_path})`,
}}
></div>
<div className='basic-info'>
{item.name ? (
<p className='title'>{item.name}</p>
) : (
<p className='title'>{item.title}</p>
)}
{item.first_air_date ? (
<p className='title'> {item.first_air_date.substring(0, 4)}</p>
) : (
<p className='title'>{item.release_date.substring(0, 4)} </p>
)}
</div>
</div>
);
export default Card;
card-collection:
import React from 'react';
import Card from '../card/card.component';
import './card-collection.styles.css';
class CardCollection extends React.Component {
goToDetails = (item) => {
localStorage.setItem('selectedItem', item);
this.props.history.push('/details');
};
render() {
console.log('PROPOVI SUUU', this.props);
return (
<div className='card-collection'>
{this.props.items.data
.filter((item, idx) => idx < 10)
.map((item) => (
<Card
key={item.id}
item={item}
onClick={() => this.goToDetails(item)}
/>
))}
</div>
);
}
}
export default CardCollection;
Ive tried multiple things, but I cant seem to pass my card props (i can work with only the id if nothing else), but I couldnt send that info via or anything else ive tried.
You need to wrap your Card component with Link tags with specified route.
<Link to="details/${id}"> <Card props={props} /> </Link>
Docs react-router Link
You're passing the onClick event as a prop to the Card component. This will not capture the click event on the card item in the list. Try wrapping the Card component in an element that will capture the click. Something like this should work:
<div onClick={() => this.goToDetails(item)}>
<Card key={item.id} item={item} />
</div>

react-router-dom i want to render the page for each element from map

Hello i have a problem with react-router-dom i set up this route in App.js ```
and i want for eaxh element from my map to go to that Singleitem component and take all the params that elements have this elements is from an api i let down my code ```
<div className="wrapper">
{this.context.dailyShop.map((item, idx) => (
<div key={idx} className="itemCard">
<div className="itemtest">
<p className="textName text">{item.item.name}</p>
<p className="textCost text">{item.store.cost}</p>
<Link to={"/single/:" + item.item.name}>
<img className="itemPhoto" src={item.item.images.background} alt="alt" />
</Link>
</div>
</div>
))}
</div>
and here is the singlepage component ```import React from 'react';
const Singleitem = ({ match }) => {
console.log(match)
return (
<div>
hello from single page item {match.params.name}
</div>
)
}
export default Singleitem ```
i only get in that component the name and i want more props like id and more

react-redux: Rendering a component after an API call

I am building an app which uses user input and shows number of recipes and they can click on recipe card to view ingredients as well. Every time they click on recipe card I make an API call to get appropriate recipe ingredient. But I am not able to figure out how to show the component which contains the recipe ingredients. I tried with conditional routing and conditional rendering as well but couldn't find the solution.
Recipe_Template.js
export class RecipeTemplate extends Component {
renderRecipe = recipeData => {
return recipeData.recipes.map(recipeName => {
return (
<div className="container">
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<a
href={recipeName.source_url}
target="_blank"
onClick={() => {
this.props.fetchRecipeId(recipeName.recipe_id);
}}
>
<img
src={recipeName.image_url}
className="mx-auto d-block img-fluid img-thumbnail"
alt={recipeName.title}
/>
<span>
<h3>{recipeName.title}</h3>
</span>
</a>
<span}>
<h3>{recipeName.publisher}</h3>
</span>
</div>
</div>
</div>
);
});
};
render() {
return (
<React.Fragment>
{this.props.recipe.map(this.renderRecipe)}
</React.Fragment>
);
}
}
Recipe_Detail.js
class RecipeDetail extends Component {
renderRecipeDetail(recipeData) {
return recipeData.recipe.ingredients.map(recipeIngredient => {
return <li key={recipeIngredient}>recipeIngredient</li>;
});
}
render() {
if (this.props.recipeId === null) {
return <div>Loading...</div>;
}
return <ul>{this.props.recipeId.map(this.renderRecipeDetail)}</ul>;
}
}
function mapStateToProps({ recipeId }) {
return { recipeId };
}
export default connect(mapStateToProps)(RecipeDetail);
Not entirely sure why you would need Redux here (unless it's being shared among other nested components), but I'm fairly certain you can just utilize React state.
One approach would be to configure your routes as such:
<Route path="/recipes" component={Recipes} />
<Route path="/recipe/:id" component={ShowRecipe} />
When the user sends a query, gets some results, and you display all matching recipes to a Recipes component. Each recipe then has a name (and other associated displayable data) and a clickable link:
<Link to={`/recipe/id?recipeId=${recipeId}`}>View {recipeName} Recipe</Link>
which for simplicity sake might look like:
<ul>
<Link to="/recipe/id?recipeId=08861626">View Prosciutto Bruschetta Recipe</Link>
<Link to="/recipe/id?recipeId=04326743">View Pasta Bundt Loaf Recipe</Link>
...etc
</ul>
When the user clicks on the link, react-router sends the user to the ShowRecipe component with a unique recipeId.
ShowRecipe then makes another AJAX request to get the recipe details:
ShowRecipe.js
export default class ShowRecipe extends Component {
state = { recipeDetail: '' }
componentDidMount = () => {
const { recipeId } = this.props.location.query; // <== only natively available in react-router v3
fetch(`http://someAPI/recipe/id?recipeId=${recipeId}`)
.then(response => response.json())
.then(json => this.setState({ recipeDetail: json }));
}
render = () => (
!this.state.recipeDetails
? <div>Loading...</div>
: <ul>
{this.state.recipeDetail.map(ingredient => (
<li key={ingredient}>ingredient</li>
)}
</ul>
)
}
Another approach:
Have the recipeDetails stored and available within the original fetched recipes JSON. Then map over the recipes and create multiple <Card key={recipeId} recipeName={recipeName} recipeDetail={recipeDetail} /> components for each recipe.
which for simplicity sake might look like:
<div>
{this.state.recipes.map(({recipeId, recipeName, recipeDetail}), => (
<Card key={recipeId} recipeName={recipeName} recipeDetail={recipeDetail} />
)}
</div>
Then each individual Card has it's own state:
Card.js
export default class Card extends Component {
state = { showDetails: '' }
toggleShowDetails = () => this.setState(prevState => ({ showDetails: !this.state.showDetails }))
render = () => (
<div>
<h1>{this.props.recipeName} Recipe</h1>
<button onClick={toggleShowDetails}> {`${!this.state.showDetails ? "Show" : "Hide"} Recipe<button>
{ this.state.showDetails &&
<ul>
{this.props.recipeDetail.map(ingredient => (
<li key={ingredient}>ingredient</li>
)}
</ul>
}
)
}
Therefore, by default the recipeDetail is already there, but hidden. However, when a user clicks the Card's button, it will toggle the Card's showDetails state to true/false to display/hide the recipe detail.

Resources