Editing parent component styles when hovering the childs component element React - javascript

**I wonder how can I edit parent component styles (backgound-color for example) when I'm hovering the child's component element - add to Cart button. Now the styles are applyed to all of the parent cards components at the same time, but I need it to be applyed only to the item's parent component. **
------------------
Parent component
------------------
const Products = ({ products }) => {
const [hovered, setHovered] = useState(false);
return (
<>
<Product products={products} hovered={hovered} setHovered={setHovered} />
</>
);
};
------------------
Child component
------------------
const Product = ({products, hovered, setHovered, style }) => {
if (hovered) {
style = {
backgroundColor: "#ffcbcb",
color: "#fff",
transition: "all 0.5s ease"
};
} else {
style = {
backgroundColor: "#ececec",
color: "#000",
transition: "all 0.5s ease"
};
}
return (
<div className={styles.items}>
{products.map((value) => {
return (
<>
<div className={styles.item} key={id} style={style}>
<button
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
className={styles.btn}
>
Add to Cart
</button>
</div>
</>
);
})}
</div>
);
};

Related

How to map components and ensure the component has the same attribute individually

Currently I have a map function that render a serial of image, and I realized that they share the same hover state, which means they will perform the same action when hovered. Is there are any standard practice to map duplicate components while assigning them unique/individual properies?
{itemData.map((item) => (
<ImageListItem key={item.img}>
<img
src={item.img}
alt={item.title}
loading="lazy"
onMouseOver={() => {setHover(true)}}
onMouseOut={() => {setHover(false)}}
style={{ transform: hover ? 'scale(1.5, 1.5)' : null }}
/>
<ImageListItemBar
title={item.title}
subtitle={item.author}
actionIcon={
<IconButton
sx={{ color: 'rgba(255, 255, 255, 0.54)' }}
aria-label={`info about ${item.title}`}
>
<InfoIcon />
</IconButton>
}
/>
You should use a component, which create a unique state for each element, i wrote an easy to understand example.
import React, { useState } from "react"
const items = [
{
title: 'Card1',
price: 100
},
{
title: 'Card2',
price: 50
},
{
title: 'Card3',
price: 200
},
]
export default function App() {
return (
<>
{
items.map(element => {
return(
<Card {...element}/>
)
})
}
</>
)
}
function Card({title, price, key}) {
const [isHovered, setHover] = useState(false)
return (
<>
<div
key={key}
onMouseOver={() => {setHover(true)}}
onMouseOut={() => {setHover(false)}}
>
<div>
{title}
</div>
<h3>
{
isHovered && price
}
</h3>
</div>
</>
);
}
I made the card price to show if hovered so you can see it works on each individual component.
Code sandbox if you want to check it out.
To provide unique properties, you need to have something that uniquely identifies your image component and use it to manage your state. In your case, your state hover should be an array or an object, not a boolean. Since you are using item.img as a key, I assume it is unique and hence it can help in your state management like this:
const [hover, setHover] = useState({});
{itemData.map((item) => (
<ImageListItem key={item.img}>
<img
src={item.img}
alt={item.title}
loading="lazy"
onMouseOver={() => setHover({...hover, [item.img]: true})}
onMouseOut={() => setHover({...hover, [item.img]: false})}
style={{ transform: hover ? 'scale(1.5, 1.5)' : null }}
/>
<ImageListItemBar
title={item.title}
subtitle={item.author}
actionIcon={
<IconButton
sx={{ color: 'rgba(255, 255, 255, 0.54)' }}
aria-label={`info about ${item.title}`}
>
<InfoIcon />
</IconButton>
}
/>
))
}
If you want the state to be in the parent without going all the way to an array or object, you can use a number instead. If only one item at a time is going to be active, you can just use the index of the active item as the state:
const { useState } = React;
const things = ["foo", "bar", "baz"];
function Component() {
const [active, setActive] = useState(-1);
const updateActivity = (index) => setActive(index === active ? -1 : index);
return (
<ul>
{things.map((thing, index) => (
<li>
<button key={index} onClick={() => updateActivity(index)}>
{index === active
? <strong>{thing}</strong>
: thing}
</button>
</li>
))}
<li>Value: {active}</li>
</ul>
);
}
ReactDOM.render(
<Component />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Alternatively, in cases where you want multiple items to be simultaneously active, you can use a "bit flag" approach where each bit of the value represents whether or not the corresponding index is active:
const { useState } = React;
const things = ["foo", "bar", "baz"];
function Component() {
const [active, setActive] = useState(0);
const updateActivity = (index) => setActive(active ^ Math.pow(2, index));
return (
<ul>
{things.map((thing, index) => (
<li>
<button key={index} onClick={() => updateActivity(index)}>
{active & Math.pow(2, index)
? <strong>{thing}</strong>
: thing}
</button>
</li>
))}
<li>Value: {active} ({active.toString(2).padStart(3, "0")})</li>
</ul>
);
}
ReactDOM.render(
<Component />,
document.getElementById("react2")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id="react2"></div>

Framer motion modal - Animate presence does not work

I am using modal with framer motion, the initial animation for the modal works fine, but on the exit it does not work at all. The modal disappears immediately for some reason.
This is where I am opening the modal:
const Agenda = () => {
const [modalOpen, setModalOpen] = useState(false);
const close = () => setModalOpen(false);
const open = () => setModalOpen(true);
return (
<>
<AnimatePresence
initial={false}
exitBeforeEnter={true}
>
{modalOpen && (
<Modal modalOpen={modalOpen} handleClose={close}>
<AgendaContent />
</Modal>
)}
</AnimatePresence>
</>
);
};
export default Agenda;
And here is the modal itself:
const newspaper = {
hidden: {
transform: "scale(0) rotate(720deg)",
opacity: 0,
transition: {
delay: 0.3,
},
},
visible: {
transform: " scale(1) rotate(0deg)",
opacity: 1,
transition: {
duration: 0.5,
},
},
exit: {
transform: "scale(0) rotate(-720deg)",
opacity: 0,
transition: {
duration: 0.3,
},
},
};
const Modal = ({ handleClose, children }) => {
return (
<Backdrop onClick={handleClose}>
<motion.div
drag
onClick={(e) => e.stopPropagation()}
className="modal"
variants={newspaper}
initial="hidden"
animate="visible"
exit="exit"
>
<img
className="close-icon"
src={CloseIcon}
alt="close"
onClick={handleClose}
/>
{children}
</motion.div>
</Backdrop>
);
};
export default Modal;
I followed this tutorial. I am not sure what I am doing wrong here, I am using the AnimatePresence there, any idea what could be wrong?
From the AnimatePresence docs:
Note: Direct children must each have a unique key prop so
AnimatePresence can track their presence in the tree.
It's frustratingly easy to forget even if you know about the requirement.
Something like this should work:
<AnimatePresence
initial={false}
exitBeforeEnter={true}
>
{modalOpen && (
<Modal key="modal" modalOpen={modalOpen} handleClose={close}>
<AgendaContent />
</Modal>
)}
</AnimatePresence>

How to update a component based on changes in another component in React

There are two components which don't have parent-child or sibling relationship between them.
One of them build the Toolbar and another one contains a color picker. The idea is to change the color of the Toolbar based on the value set in the color picker.
Here is my code so far:
import React from 'react';
import { Button, Icon } from 'semantic-ui-react';
import { ChromePicker } from 'react-color';
export default class Banner extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
displayColorPicker: false,
background: '#fff',
};
}
handleClick = () => {
this.setState({ displayColorPicker: true });
};
handleClose = () => {
this.setState({ displayColorPicker: false });
};
handleChange = color => {
this.setState({ background: color.hex });
};
handleChangeComplete = color => {
this.setState({ background: color.hex });
};
render() {
const popover = {
position: 'absolute',
zIndex: '2',
};
const cover = {
position: 'fixed',
top: '0px',
right: '0px',
bottom: '0px',
left: '0px',
};
return (
<div className="banner-container settings-banner">
<table className="settings-banner-container">
<tbody>
<tr className="setttings-container-tr">
<div
className="xx"
style={{ backgroundColor: this.state.background }}>
<div className="title-cell-value settings-banner-title">
Brand color
</div>
<div>
<Button onClick={this.handleClick}>Pick Color</Button>
{this.state.displayColorPicker ? (
<div style={popover}>
<div
style={cover}
onClick={this.handleClose}
onKeyDown={this.handleClick}
role="button"
tabIndex="0"
aria-label="Save"
/>
<ChromePicker
color={this.state.background}
onChange={this.handleChange}
onChangeComplete={this.handleChangeComplete}
/>
</div>
) : null}
</div>
</div>
</tr>
</tbody>
</table>
</div>
);
}
}
In the above file, the ChromePicker is used to choose a color and save its value in this.state.background. I'm using that value to update the color of div with class xx. This works good, the div's color is updated directly.
However, I don't know how to "export" that color value outside and use it in another component.
In this case it would be the Toolbar, I want to send the value from this.state.background to the style = {{ .. }}
Is there a way to do it?
import React from 'react';
import Logo from '../Logo/Logo';
export default class Toolbar extends React.PureComponent {
render() {
return (
<div className="corporate-toolbar" style={{ backgroundColor: 'green' }}>
<Logo corporate />
</div>
);
}
}
There is many ways to do it
You can use context(best solution), redux(if you app is really big) or just move the property to the common parent and pass it to components (it's the worst way, not recommended)
Documentation for context - https://reactjs.org/docs/context.html
Documentation for redux - https://react-redux.js.org
A simple example of using context https://www.digitalocean.com/community/tutorials/react-usecontext
Here is a working example using context:
//in file ColorContext.js (should export but breaks snippet)
const ColorContext = React.createContext();
const ColorProvider = ({ children }) => {
const [color, setColor] = React.useState('#fff');
return (
<ColorContext.Provider value={{ color, setColor }}>
{children}
</ColorContext.Provider>
);
};
//in file Banner.js
class Banner extends React.PureComponent {
handleChange = (color) => {
this.context.setColor(color);
};
render() {
return (
<div style={{ backgroundColor: this.context.color }}>
<select
value={this.context.color}
onChange={(e) =>
this.handleChange(e.target.value)
}
>
<option value="#fff">fff</option>
<option value="#f00">f00</option>
<option value="#f0f">f0f</option>
</select>
</div>
);
}
}
//ColorContext is imported from ColorContext.js
Banner.contextType = ColorContext;
//in file Toolbar.js
class Toolbar extends React.PureComponent {
render() {
return (
<h1 style={{ backgroundColor: this.context.color }}>
Toolbar
</h1>
);
}
}
//ColorContext is imported from ColorContext.js
Toolbar.contextType = ColorContext;
const App = () => (
<div>
<Banner />
<Toolbar />
</div>
);
ReactDOM.render(
<ColorProvider>
<App />
</ColorProvider>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

How to render React component with passed in props on click

I am making a Menu using React and Redux but am currently having an issue rendering the items when the user selects a category.
My goal is to render a list of items nested in the redux state of a particular category when it is clicked.
In Categories.js, it loads the categories from the redux store and displays them.
import React, { Component } from "react";
import { connect } from "react-redux";
import CategoryItems from "./CategoryItems"
import {
Card,
CardTitle,
CardImg,
Container,
Row,
Col,
CardBody,
Button
} from "shards-react";
class Categories extends Component {
handleClick = category => {
alert(category.title);
return (
<CategoryItems
category={category}
/>
);
};
render() {
let categoryList = this.props.categories.map(category => {
return (
<div key={category.id}>
<Col>
<Card
key={category.id}
onClick={() => {
this.handleClick(category);
}}
>
<CardBody>
<CardTitle>{category.title}</CardTitle>
</CardBody>
</Card>
</Col>
</div>
);
});
return (
<Container>
<Row>{categoryList}</Row>
</Container>
);
}
}
const mapStateToProps = state => {
return {
categories: state.categories,
};
};
export default connect(mapStateToProps)(Categories);
When a category is clicked, (the alert was just so I could make sure data made it) I have it set to render the items Array nested within the selected category.
import React, { Component } from 'react'
import {
Card,
CardTitle,
CardImg,
Container,
Row,
Col,
CardBody,
Button
} from "shards-react";
export class CategoryItems extends Component {
render() {
let items = this.props.category.items;
let categoryItems = items.map(item => {
return (
<Col className="">
<Card
className="mt-2 mb-2 item-col"
style={{ maxWidth: "500px" }}
key={item.id}
>
<CardBody>
<CardTitle style={{ position: "absolute", top: 20, right: 20 }}>
{item.title}
</CardTitle>
<CardImg
style={{ maxWidth: "200px" }}
src={item.img}
alt={item.title}
/>
<span
style={{ position: "absolute", bottom: 40, right: 100 }}
to="/"
// onClick={() => {
// this.handleClick(item.id);
// }}
>
<Button pill theme="info">
+
</Button>
</span>
<div style={{ position: "absolute", bottom: 40, right: 20 }}>
${item.price}
</div>
</CardBody>
</Card>
</Col>
);
});
return (
<Container className="menu-item-cont">
<Row>{categoryItems}</Row>
</Container>
);
}
}
export default CategoryItems
This part does not render the items and I don't get an error message.
I have also tried placing the items I would like rendered directly into the state just to see if I could get them to render on click similar to how the Categories.js is with no luck.
I am somewhat new to react so please forgive me if this is a beginner question.
Any help is appreciated as I have spent hours trying to figure this out.
The problem is that you're not rendering what gets returned from handleClick. What you need is a conditional render in the JSX, then determine whether or not to render the elements depending on a controlled flag variable in the state.
class Categories extends Component {
constructor() {
this.state = {
show: false,
category: undefined
}
this.toggle = (category) => {
this.setState({ show: !this.state.show, category })
}
}
render() {
let categoryList = this.props.categories.map(category => {
return (
<div key={category.id}>
<Col>
<Card
key={category.id}
onClick={e => this.toggle(category)}
>
<CardBody>
<CardTitle>{category.title}</CardTitle>
</CardBody>
</Card>
</Col>
</div>
);
});
return ([
<Container>
<Row>{categoryList}</Row>
</Container>,
(
this.state.show
? <CategoryItems category={this.state.category}/>
: null
)
]);
}
}

How re-render just copmonent to which I clicked - react

I'm trying to make a simple color memory game in React (find where there are two identical images).
When I click on one card all of the other cards re-render. How can I prevent this?
//app class
handleClick = index => {
this.setState((prevState) => {
var temp = [...prevState.clickedPicture, index]
return{clickedPicture: temp}
})
}
isClicked = (index) => this.state.clickedPicture.indexOf(index) === -1
render() {
return(
<div className="content">
<div className="header">
<h1>Memory</h1>
</div>
<div className="main">
_.shuffle(this.state.colors).map((current,index) =>
<Game
key={index}
index={index}
current={current}
status={this.state.status}
handleClick={this.handleClick}
bool={this.isClicked(index)}
/>)
</div>
}
// Game component
class Game extends Component {
clickHandle = () => {
if(this.props.bool){
this.props.handleClick(this.props.index)
}
}
render() {
return(
<div className={this.props.status}
style={{ backgroundColor: this.props.bool ?
'black' : this.props.current }}
onClick={this.clickHandle}>
</div>
);
}
}
Make Cards their own component so they have access to lifecycle hooks. then
make the Cards a PureComponents, therefore they will only update if the reference to a prop changes.

Categories

Resources