I am facing this problem. I want to show hover box on this box which is chosen. F.e when I hover on Box One I want to show Hover One, Box Two -> Hover Two. But in my example when I hover on One both are displayed. I am trying to do this with refs or e.target but always something is not as I want.
Link to stackblitz: https://stackblitz.com/edit/react-hc4741?file=src/App.js
import React, { useState } from "react";
import "./style.css";
import { BooksSection, BookCard, BookCardHover } from "./Styled";
export default function App() {
const [displayBookCardHover, setDisplayBookCardHover] = useState(false);
const showCardHover = () => {
setDisplayBookCardHover(true);
};
const hiddenCardHover = () => {
setDisplayBookCardHover(false);
};
return (
<div>
<BooksSection>
<BookCard
bgColor={"#000"}
color={"#fff"}
onMouseEnter={showCardHover}
onMouseLeave={hiddenCardHover}
>
<BookCardHover display={displayBookCardHover}>
Hover One
</BookCardHover>
Box One
</BookCard>
<BookCard
bgColor={"#fff"}
color={"#000"}
onMouseEnter={showCardHover}
onMouseLeave={hiddenCardHover}
>
<BookCardHover display={displayBookCardHover}>
Hover Two
</BookCardHover>
Box Two
</BookCard>
</BooksSection>
</div>
);
}
styled components
import styled from "styled-components";
export const BooksSection = styled.div`
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100wh;
`;
export const BookCard = styled.div`
width: 50%;
height: 500px;
padding: 20px 0;
background: ${props => props.bgColor};
color: ${props => props.color};
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
`;
export const BookCardHover = styled.div`
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
font-size: 50px;
background: rgba(0, 0, 0, 0.7);
visibility: ${({ display }) => (display ? "100" : "hidden")};
`;
The problem is you have the exact same component with the exact same prop value in both places, so they will be shown/hidden at the same time no matter what you do to the displayBookCardHover value.
The trick is to use a separate value for each. Like this:
const [hoverIndex, setHoverIndex] = useState(-1);
...
const showCardHover = (index) => {
setHoverIndex(index);
}
const hiddenCardHover = () => {
setHoverIndex(-1);
}
...
<BookCard
...
onMouseEnter={() => showCardHover(0)}
...
>
<BookCardHover display={hoverIndex === 0}>
...
<BookCardHover display={hoverIndex === 1}>
Hope you get the idea.
On a side note, there's no "100" value for visibility prop. It's either "hidden" or "visible".
import React, { useState } from "react";
import "./style.css";
import { BooksSection, BookCard, BookCardHover } from "./Styled";
export default function App() {
const [displayBookCardHover, setDisplayBookCardHover] = useState({
boxOneHover: false,
boxTowHover: false
});
const showCardHover = box => {
if (box === 1) {
setDisplayBookCardHover(ps=>({ ...ps, boxOneHover: true }));
} else {
setDisplayBookCardHover(ps=>({ ...ps, boxTowHover: true }));
}
};
const hiddenCardHover = box => {
if (box === 1) {
setDisplayBookCardHover(ps=>({ ...ps, boxOneHover: false }));
} else {
setDisplayBookCardHover(ps=>({ ...ps, boxTowHover: false }));
}
};
return (
<div>
<BooksSection>
<BookCard
bgColor={"#000"}
color={"#fff"}
onMouseEnter={() => showCardHover(1)}
onMouseLeave={() => hiddenCardHover(1)}
>
<BookCardHover display={displayBookCardHover.boxOneHover}>
Hover One
</BookCardHover>
Box One
</BookCard>
<BookCard
bgColor={"#fff"}
color={"#000"}
onMouseEnter={() => showCardHover(2)}
onMouseLeave={() => hiddenCardHover(2)}
>
<BookCardHover display={displayBookCardHover.boxTowHover}>
Hover Two
</BookCardHover>
Box Two
</BookCard>
</BooksSection>
</div>
);
}
I think that the BookCard should be a component. Each one should have its own state. In App.js you can use BookCard and pass bgColor and color and whatever you want to customize each BookCard as props and use them in it.
The issue with your code is - both of the BookCardHover component base their state of the display using the same reference state displayBookCardHover, so, when one changes the value of displayBookCardHover, it automatically reflect on the other. I would recommend the approch suggested by #technophyle to seperate them.
Related
I am trying to add styles to the inidividual elements. I want when an individual div( is clicked to make sure that the paragraph of the specific changes from display: none to dislay:block. But the problem is, when I click only on one , all the paragraph text is displayed.
here is the css below:
.cards{
background-color: red;
display: flex;
justify-content: center;
align-items: center;
height: 20vh;
}
[class*=cardDisplay]{
height: 80%;
display: flex;
align-items: center;
justify-content: center;
width: 200px;
background: black;
color: white;
margin: auto;
}
[class*=card-content]{
display: none;
}
[class*=card-content-2]{
display: block;
}
and the react.js file:
import React from 'react';
import {useState} from "react";
import "./game.css";
function Game() {
const [styling, setStyling] = useState("card-content")
const handleClick = () => {
setStyling("card-content-2")
}
return (
<div className="cards">
<div className="cardDisplay card-1" onClick={handleClick}>
<p className={styling}>Win</p>
</div>
<div className="cardDisplay card-2" onClick={handleClick}>
<p className={styling}>Sorry, No Win</p>
</div>
<div className="cardDisplay card-3" >
<p className={styling} onClick={handleClick}>Sorry, No Win</p>
</div>
</div>
)
}
You're using only one styling that you're applying to all paragraphs. If you want distinct styling for each paragraph, you have to hold that state separately for each paragraph.
Either:
Make the div+paragraph a component that holds its own state, or
Have multiple styling state members (perhaps an array).
I would lean toward the first:
function Card({className = "", children}) {
const [styling, setStyling] = useState("card-content");
const handleClick = () => {
setStyling("card-content-2")
};
return (
<div className={`cardDisplay ${className}`} onClick={handleClick}>
<p className={styling}>{children}</p>
</div>
);
};
function Game() {
return (
<div className="cards">
<Card className="card-1">Win</Card>
<Card className="card-2">Sorry, No Win</Card>
<Card className="card-3">Sorry, No Win</Card>
</div>
);
}
There's lots of ways to spin that, but that's the basic idea.
But there's also an argument for the cards being an array of objects with state; here's an example with the results randomized when the component is first mounted:
function makeCards() {
const num = 3;
const win = Math.floor(Math.random() * num);
return Array.from({length: 3}, (_, index) => ({
id: `card-${index}`,
result: index === win ? "Win" : "Sorry, No Win",
showing: false,
}));
}
function Game() {
const [cards, setCards] = useState(makeCards);
const handleClick = (event) => {
const id = event.currentTarget.getAttribute("data-id");
setCards(prevCards => {
const newCards = prevCards.map(card => {
if (card.id === id) {
return {...card, showing: true};
}
return card;
});
return newCards;
});
};
return (
<div className="cards">
{cards.map(({id, result, showing}) => (
<div key={id} data-id={id} className={`cardDisplay ${id}`} onClick={handleClick}>
<p className={showing ? "card-content" : "card-content-2"}>{result}</p>
</div>
))}
</div>
);
}
This may help you. It really works........
import React from 'react';
import {useState} from "react";
import "./game.css";
function Game() {
const handleClick = (e) => {
e.target.firstChild?.classList?.remove("card-content");
e.target.firstChild?.classList?.add("card-content-2");
};
return (
<div className="cards">
<div className="cardDisplay card-1" onClick={handleClick}>
<p className="card-content">Win</p>
</div>
<div className="cardDisplay card-2" onClick={handleClick}>
<p className="card-content">Sorry, No Win</p>
</div>
<div className="cardDisplay card-3" onClick={handleClick}>
<p className="card-content">Sorry, No Win</p>
</div>
</div>
);
}
What you are currently doing is changing the styling for each of the p tags.
I will advise you have a custom class that returns your div tag that contains the p tag
Have an array of Objects that contain the card style and the text that will be displayed. Map through the array and return the style and tags accordingly. As displayed below.
handle the click events in the custom div.
import React from 'react';
import "./game.css";
import MyDiv from './MyDiv';
const cards = [{ cardStyle: 'card-1', text: "Win" },
{ cardStyle: 'card-2', text: 'Sorry, No Win' },
{ cardStyle: 'card-3', text: 'Sorry, NoWin' }];
function Game() {
return (
<div className="cards">
{cards.map((card, index) =>
(<MyDiv cardNumberStyle={card.cardStyle}
key={index}
textToDisplay={card.text} />))
}
</div>
);
}
export default Game;
And then MyDiv is:
import React, { useState } from "react";
const MyDiv = ({ cardNumberStyle, textToDisplay }) => {
const [styling, setStyling] = useState('card-content');
///Handles clicks
const handleClick = () => {
//conditionally change the styling
if (styling === 'card-content')
setStyling('card-content-2')
else setStyling('card-content');
}
return (
<div className={`cardDisplay ${cardNumberStyle}`} onClick={handleClick}>
<p className={styling}>{textToDisplay}</p>
</div>
);
}
export default MyDiv;
It appears I cannot console.log any selection from an onClick for this React component. I am trying to basically achieve the following.
https://codesandbox.io/s/great-ardinghelli-6seko?file=/src/demoComponent.jsx
The data is fetched from redux and appears in the frontend (I have this working).
I would like to achieve that when you select a line, it just console.logs('you have selected'role).
However it doesn't appear I can get ANY data to trigger on an onClick when it's a mapped data source.
I have implemented a p tag which fires and works as expected.
See the following refined code:
import React, { useRef } from "react";
import styled from "styled-components";
const SearchContent = styled.div`
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
padding: 1em;
overflow-y: auto;
`;
const JobContainer = styled.div`
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
padding: 5px 5px;
`;
export function SearchBarTrialRedux(props) {
const inputRef = useRef();
const [term, setTerm] = useState("")
const handleClickRole = () => {
console.log("Hello, you have selected" )
}
const searchResults = useSelector(state => state.jobsearch.roles.map(role => role.title))
const SearchResultsText = ({ text }) => {
return (
<JobContainer onClick={()=> {
console.log("Hello")
}}>
<JobSearchTypography
>
{text}
</JobSearchTypography>
</JobContainer>
);
};
return (
<SearchContent>
<JobContainer >
{searchResults && searchResults.length < 3 && searchResults.map((result) =>
(<IndividualContainer handleClick={handleClickRole}>
<SearchResultsText text={result}/>
</IndividualContainer>
)
)}
</JobContainer>
<p onClick={handleClickRole}>
fff
</p>
</SearchContent>
)
Does anyone know why this doesn't work + how to achieve the solution? I'm a little lost here!
As it can be seen from your code snippet the handleClickRole function just prints to the console the message "Hello, you have selected". There is no variable passed on it in order to print it.
This can be changed like this:
const handleClickRole = (role) => {
console.log(`Hello, you have selected ${role}`);
}
Furthermore, the way you call the above function on the onClick event I think is incomplete. It would be better if it looked like this:
<p onClick={() => handleClickRole(role)}>fff</p> // considering that you want to pass a variable with the role
or
<p onClick={() => handleClickRole()}>fff</p> // without a variable
Please also look at the below example:
import { useState } from "react";
export default function App() {
const [myInput, setMyInput] = useState("");
const [isButtonClicked, setIsButtonClicked] = useState(false);
const handleChange = (event) => {
setIsButtonClicked(false);
setMyInput(event.target.value);
};
const handleClick = () => {
setIsButtonClicked(true);
console.log(myInput);
};
return (
<div className="App">
<input
onChange={handleChange}
id=".myInput"
placeHolder="Enter Something"
/>
<button
style={{
display: "block",
width: "100px",
margin: "10px auto",
}}
onClick={() => handleClick()}
>
OK
</button>
<p style={{ fontWeight: "bold" }}>
The entered value is:
<span style={{ fontWeight: "400" }}>
{isButtonClicked ? ` ${myInput}` : null}
</span>
</p>
</div>
);
}
the <IndividualContainer>...</IndividualContainer> is a component created by you right?
if yes, then you will have to have to call the onClick function in the component like so:
inside the IndividualContainer component
...
// the outer container
<div onClick={props.handleClick}>
...
</div>
...
lemme know if it works
I just edited my answer now
I was wondering how would I approach to change a body background image with onClick Event. Should I be using useRef hook or. I would really appriciate the help
body {
background: url("http");
background-position: center;
background-size: cover;
overflow-y: hidden;
overflow-x: hidden;
min-width: 100%;
}
function App() {
return (
<div>
<h1 onClick={...}> click here to change background image </h1>
</div>
);
}
export default App;
You can simply use document.body.style.backgroundImage like this:
import { useState } from "react";
import "./styles.css";
export default function App() {
const [counter, setCounter] = useState(0);
const IMAGES = [
"https://cdn.pixabay.com/photo/2012/08/25/22/22/saturn-54999_1280.jpg",
"https://cdn.pixabay.com/photo/2020/07/06/01/33/sky-5375005_1280.jpg",
"https://cdn.pixabay.com/photo/2011/12/14/12/23/solar-system-11111_1280.jpg"
];
const changeBodyBgImage = () => {
document.body.style.backgroundImage = `url(${IMAGES[counter]})`;
setCounter(counter > 2 ? 0 : counter + 1);
};
return (
<div className="App">
<h1 onClick={changeBodyBgImage}>Hello CodeSandbox</h1>
</div>
);
}
Here is the demo: https://codesandbox.io/s/serene-brook-82prg?file=/src/App.js:0-665
Use document.body to set the background changes you need.
The event handler should look like this.
onClick={() => {
document.body.style.background = ...
}}
enter image description here
I try to change button color via applying styles.css to button component. Component button got className for switching from reducer. After done condition in reducer className in button component got its correct value, but color not switch.... any ideas why?
please see
"
button key={id} className ={props.optionStyle}
"
reduser.js
....
if (newOption.option === answer) {
return {
...state,
userAnswer: newOption.option,
answer: answer,
answerTrue: true,
currentSrc: currentSrcT,
questAudio: false,
optionStyle: "styles.correct"
};
......
component with button
import React from 'react';
import styles from './item.css';
const OptionItem = (props) => {
const { option, image, id} = props.optionAnswer ;
const { onChoiseOption} = props ;
const onButtonChose = (id) =>{
if(props.answerTrue) {
}
}
const onChoiseOptionComp = e => {
onChoiseOption(),
onButtonChose(id)
}
return (
<div>
<button key={id} className={props.optionStyle}
onClick={
onChoiseOptionComp
}
>
{option}
<p ><img src={image} alt="cartinka" className={styles.imageOption}/></p>
</button>
</div>
)
};
item.css
.imageOption {
width: 300px;
padding-bottom: 10px;
}
.optionChose {
background-color: blue;
}
.correct {
background-color: crimson;
}
.nonCorrect {
background-color: #2ddc66;
}
.....if I tried to change className manually - that work fine
I think you are trying to use CSS modules in runtime code, and this won't work because it gets resolved before the code is built/ran.
optionStyle: "correct" in reducer looks more reasonable.
Secondly, your reducer gives you a string value "styles.correct" that is not the same as the variable styles.correct. This is one way to fix it:
className={ props.optionStyle === "correct" ? styles.correct : styles.nonCorrect }
I just read in the styled-components documentation that the following is wrong and it will affect render times. If that is the case, how can I refactor the code and use the required props to create a dynamic style?
Thank you in advance.
Tab component
import React from 'react'
import styled from 'styled-components'
const Tab = ({ onClick, isSelected, children }) => {
const TabWrapper = styled.li`
display: flex;
align-items: center;
justify-content: center;
padding: 100px;
margin: 1px;
font-size: 3em;
color: ${props => (isSelected ? `white` : `black`)};
background-color: ${props => (isSelected ? `black` : `#C4C4C4`)};
cursor: ${props => (isSelected ? 'default' : `pointer`)};
`
return <TabWrapper onClick={onClick}>{children}</TabWrapper>
}
export default Tab
I believe what the documentation is saying is that you should avoid including your styles inside of the rendering component:
DO THIS
const StyledWrapper = styled.div`
/* ... */
`
const Wrapper = ({ message }) => {
return <StyledWrapper>{message}</StyledWrapper>
}
INSTEAD OF THIS
const Wrapper = ({ message }) => {
// WARNING: THIS IS VERY VERY BAD AND SLOW, DO NOT DO THIS!!!
const StyledWrapper = styled.div`
/* ... */
`
return <StyledWrapper>{message}</StyledWrapper>
}
Because what happens is when the component's Props changes, then the component will re-render and the style will regenerate. Therefore it makes sense to keep it separate.
So if you read further on to the Adapting based on props section, they explain this:
const Button = styled.button`
/* Adapt the colours based on primary prop */
background: ${props => props.primary ? "palevioletred" : "white"};
color: ${props => props.primary ? "white" : "palevioletred"};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
// class X extends React.Component {
// ...
render(
<div>
<Button>Normal</Button>
<Button primary>Primary</Button>
</div>
);
// }
this works because when you use the Button component in class X, it will know the props of class X without you having to tell it anything.
For your scenario, I imagine the solution would be simply:
const TabWrapper = styled.li`
display: flex;
align-items: center;
justify-content: center;
padding: 100px;
margin: 1px;
font-size: 3em;
color: ${props => (props.isSelected ? `white` : `black`)};
background-color: ${props => (props.isSelected ? `black` : `#C4C4C4`)};
cursor: ${props => (props.isSelected ? 'default' : `pointer`)};
`;
const Tab = ({ onClick, isSelected, children }) => {
return <TabWrapper onClick={onClick}>{children}</TabWrapper>
}
const X = <Tab onClick={() => console.log('clicked')} isSelected>Some Children</Tab>
I haven't tested this at all, so please feel free to try it out and let me know if it works for you or whatever worked for you!
You can pass an argument with Typescript as follows:
<StyledPaper open={open} />
...
const StyledPaper = styled(Paper)<{ open: boolean }>`
top: ${p => (p.open ? 0 : 100)}%;
`;
Another way to do it would be
const StyledDiv = styled.div.attrs((props: {color: string}) => props)`
width: 100%;
height: 100%;
background-color: ${(props) => props.color};
`
//...
render() {
return (
<StyledDiv color="black">content...</StyledDiv>
);
}
This way you are type-safe in terms of the props you want to send into the styled component. (Good when coding in Typescript)
For a more simple example with functional components:
Suppose you have an arrow like polygon and you need 2 of them pointing in different directions. So you can pass the rotate value by props
<Arrow rotates='none'/>
<Arrow rotates='180deg'/>
Then in the Component Arrow you have to pass the props like normal component to the styled component but in the styled component you have to use it like props:
import React from 'react';
import styled from "#emotion/styled";
const ArrowStyled = styled.div`
background-color: rgba(255,255,255,0.9);
width: 24px;
height: 30px;
clip-path: polygon(56% 40%,40% 50%,55% 63%,55% 93%,0% 50%,56% 9%);
transform: rotate(${props => props.rotates});
`
const Arrow = ({rotates}) => {
return (
<ArrowStyled rotates={rotates}/>
);
}
export default Arrow;
If you're using Typescript create an interface inside your styles file!
Otherwise, you won't be able to access props in your CSS
import styled from 'styled-components'
interface StyledLiProps{
selected: boolean
}
export const TabWrapper = styled.li`
// styles ...
color: ${props => (selected ? `white` : `black`)};
background-color: ${props => (selected ? `black` : `#C4C4C4`)};
`
And don`t forget to declare the props you want to use in your CSS inside your JSX
interface TabProps{
text: string;
}
const Tab = ({ text }: TabProps) => {
//...
return <TabWrapper selected={isSelected} onClick={() => updateTab}>{text}</TabWrapper>
}
Consider styled components documentation gives example of using reacts context api [2] for different themes.
[1] https://www.styled-components.com/docs/advanced
[2] https://reactjs.org/docs/context.html
Exporting styled-component
Button
and passing scrollPosition as a prop in functional component
PassingPropsToSyledComponent
import styledComponents from "styled-components";
export const Button = styledComponents.div`
position: ${ props => props.scrollPosition ? 'relative' : 'static' };
`;
export const PassingPropsToSyledComponent = ()=> {
return(
<Button scrollPosition={scrollPosition}>
Your Text Here
</Button>
)
}