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
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;
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.
In a class, I would write: this.prop.bind(this,arg)
like this:
<button onClick={this.props.delete.bind(this, id)} id='deletebtn'>
X
</button>
but how would I do the same thing in a function?
I need to use a class or there is a way to do it without it?
ps: did hooks killed classes? if not when are classes really necessary?
You want to create a function that returns a function, like this:
function deleteItem(id) {
return function() {
... <using `id`>
}
}
or with arrow functions:
const deleteItem = (id) => {
return () => {
... <using `id`>
}
}
or even shorter:
const deleteItem = (id) => () => {
... <using `id`>
}
and then you do
<button onClick={deleteItem(id)} id='deletebtn'>
where deleteItem(id) is now a function.
To answer your question about hooks, they did not "kill classes" but provide a arguably better alternative only for React components. Class components are still supported. It is hard to imagine a situation that cannot be handled by the new functional components with hooks, though.
You can pass the removeItem handler down to the child component -
const { useState } = React
const initialItems =
[ "muffins", "cake", "pies" ]
const MyApp = ({ init = initialItems }) => {
const [items, setItems] = useState(init)
const removeItem = (pos = 0) => event =>
setItems([...items.slice(0, pos), ...items.slice(pos + 1)])
const addItem = event =>
event.key === "Enter"
? setItems([...items, event.target.value ])
: null
return <div>
{items.map((name, i) =>
<Item name={name} onDelete={removeItem(i)} />
)}
<div className="item">
<input onKeyDown={addItem} placeholder="Type here and press [ENTER]..." />
</div>
</div>
}
const Item = ({ name = "", onDelete }) =>
<div className="item">
{name}
<button onClick={onDelete}>🗑️</button>
</div>
ReactDOM.render(<MyApp />, document.body)
body {
font-family: monospace;
}
.item {
background-color: ghostwhite;
padding: 0.5rem;
}
input {
display: block;
box-sizing: border-box;
width: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
Okay, I found a way,
Like #JulienD Said it, I just need it to create a function that returns a function
this is how I did it.
the child sends the argument to the parent
<button onClick={() => {props.delPost(id)}}>delete</button>
the parent sends the argument up one more level
<Post delPost={(id)=>{props.delPost(id)}} key={post.id} postItem={post} />)
finally the root parent make use of the argument
const deletePost = (id) => {
console.log(id);
}
<Feed delPost={deletePost} posts={posts} />
I read a couple of answers and all of them seems to assume you have a single object containing CSS instead of single properties, I tried the answers and I couldn't make it work, I was wondering what's wrong and what's the best way to do this, here is what I have done so far :
import React from 'react';
import FormLabel from '#material-ui/core/FormLabel';
const label = (props) => {
// <label onClick={props.onClick} className={props.className + ' ' + props.gridClass} style={props.inlineStyle} >{props.label}</label>
let divStyleArray = [];
if (typeof props.inlineStyle.background !== 'undefined') {
divStyleArray.push(props.inlineStyle.background)
delete props.inlineStyle.background
}
if (typeof props.inlineStyle.textAlign !== 'undefined') {
divStyleArray.push(props.inlineStyle.textAlign)
delete props.inlineStyle.textAlign
}
const customStyle = {
width: '100%'
}
const divStyle = Object.assign({}, ...divStyleArray);
return (
<div className={props.gridClass} style={{divStyle}}>
<FormLabel component="label" onClick={props.onClick} style={{ ...customStyle, ...props.inlineStyle }}>{props.label}</FormLabel>
</div>
)
}
export default label;
My goal is to extract a couple of CSS property, give it to the div and then give the rest to whats inside the div
Update 01:
I tried the answered given but it doesn't seem to work properly, here is what i did:
import React from 'react';
import FormLabel from '#material-ui/core/FormLabel';
const label = (props) => {
let inlineStyle = {
...props.inlineStyle
}
const divStyle = {
background: inlineStyle.background,
textAlign: inlineStyle.textAlign,
}
delete inlineStyle.background;
delete inlineStyle.textAlign;
const customStyle = {
width: '100%'
}
return (
<div className={props.gridClass} style={divStyle}>
<FormLabel component="label" onClick={props.onClick} style={{ ...customStyle, ...inlineStyle }}>{props.label}</FormLabel>
</div>
)
}
export default label;
First of all deleting stuff from the props object would be anti-pattern, or at least bad practice as far as I know.
If you only need the two properties you use there you could use this code:
const label = (props) => {
let divStyle = {
background: props.background,
textAlign: props.textAlign,
};
const customStyle = {
width: '100%'
}
return (
<div className={props.gridClass} style={{divStyle}}>
<FormLabel
component="label"
onClick={props.onClick}
style={{
...customStyle,
...props.inlineStyle,
background: undefined,
textAlign: undefined,
}}
>{props.label}</FormLabel>
</div>
)
}
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>
)
}