ReactJS clicking button - reactjs

I am hoping that you can help me with this task, I just want to create a Like button, the initial counter for the button is 100, if the button is clicked it will add 1 and it will highlight (you can use the classname tool) and if the user undo their like it will decrease by one and it will not highlight
import cx from 'classnames';
import { Component } from 'react';
export default class LikeButton extends Component {
state = {
active: false,
count: 100
}
formatCount() {
const {count} = this.state.count
return count === 100 ? 100 : 101
}
render() {
return (
<>
<div>
<button className={this.state.active === false ? 'like' : 'like-button'}>Like | {this.formatCount()}</button>
</div>
<style>{`
.like-button {
font-size: 1rem;
padding: 5px 10px;
color: #585858;
}
.liked {
font-weight: bold;
color: #1565c0;
}
`}</style>
</>
);
}
}

Here's how to do it using functional components in React (which I'd strongly recommend):
import "./like.css"
export default function Like(){
const [count, setCount] = useState(100);
const [active, setActive] = useState(false);
function handleClick(){
if(active) {
// handle unliking
setActive(false);
setCount(oldCount => oldCount - 1);
} else {
// handle liking
setActive(true);
setCount(oldCount => oldCount + 1);
}
}
return(
<div>
<button
className={active ? 'liked' : 'unliked'}
onClick={handleClick}
>
Like | {count}
</button>
</div>
)
}
and like.css contents:
.unliked {
font-size: 1rem;
padding: 5px 10px;
color: #585858;
}
.liked {
font-weight: bold;
color: #1565c0;
}
You can rework this to the class-based approach pretty easily (though I'd recommend using functional components, they're really cool). Basically, you keep track of the count and whether or not they've already pressed the like button, and conditionally handle onClick based on the current state.

Related

How to toggle button color with className in react?

I am trying to toggle a button with hook, classname and id but all buttons toggle color instead of one button that is actually clicked.
The classname is to toggle classname "dark" and null, where .dark changes the button black.
[duplicate]
I am trying to toggle a button with hook, classname and id but all buttons toggle color instead of one button that is actually clicked.
The classname is to toggle classname "dark" and null, where .dark changes the button black.
import './Clothing.css'
import data from '../../data/data2.json';
const Clothing = () => {
const [toggle, setToggle] = useState(null);
const types = [
{ id: 11, value: 'All' },
{ id: 22, value: 'Cap' },
{ id: 33, value: 'Sweatshirt' },
{ id: 44, value: 'Hoodie' },
{ id: 55, value: 'Shirt' }
]
const handleToggle = (e) => {
console.log(e.target.id)
if (types.filter(
(item) => item.id === e.target.id
)) return setToggle(!toggle)
}
<div className="buttonDiv">
{
types.map((t) =>
<button
key={t.id}
id={t.id}
value={t.value}
className={ toggle ? "dark" : null}
onClick={(e) => {
handleSelectedCategory(e);
handleToggle(e);
}}>
{t.value}
</button>
)
}
</div>
.clothSection {
position: absolute;
top: 25%;
width: 100%;
padding: 0 2rem;
.topMenu {
display: flex;
flex-direction: column;
padding: 2rem 4rem;
.buttonDiv {
gap: 2rem;
display: flex;
padding: 2rem 0;
button {
background-color: var(--InputColor);
padding: .5rem 1rem;
border-radius: .5rem;
font-size: var(--NormalFontSize);
color: var(--TextColor);
cursor: pointer;
border: none;
}
.dark {
background-color: var(--BlackColor);
color: var(--WhiteColor);
}
It is my understanding that you have several buttons. You wish to click a button and have that button dynamically add the .dark class, giving each button its own independent state.
The issue is that you have toggle and setToggle happening in a parent component. Then you render all of your buttons with the current value of toggle. We want each button to contain its own toggle value.
New ClothingItem.js
I added a new component ClothingItem.js, which is responsible for rendering a single clothing item. Notice how this component tracks and sets its own toggle value, utilizing most of the code you had in place to render a button initially.
const ClothingItem = ({ myKey, id, value }) => {
const [toggle, setToggle] = useState(false);
return (
<button
key={myKey}
id={id}
value={value}
className={toggle ? "dark" : null}
onClick={() => {
// handleSelectedCategory(e);
setToggle(!toggle);
}}
>
{value}
</button>
);
};
Updated Clothing.js
We removed all the existing state and the handleToggle() function. In addition, instead of rendering <button>s, we now render <ClothingItem />s, passing in key, id, and value as before.
return (
<div className="buttonDiv">
{types.map((t) => (
<ClothingItem key={t.id} id={t.id} value={t.value} />
))}
</div>
);
Hello you should use classnames like this:
classNames({ dark: toggle })

How to change button background color when I click the button in React using Hooks

I am working on a React project, In my project I have two buttons, for First button I assigned a state for second button I written a function and I assigned a state as well. but my onClick function is not working. please help me to resolve this isssue.
This is App.js
import React, { useState } from "react";
import { Button } from "antd"
import 'antd/dist/antd.css';
import "./App.css";
const App = () => {
const [buttonOne, setButtonOne] = useState("red")
const [buttonTwo, setButtonTwo] = useState("blue")
const buttonTwoBackgroundColor = () => {
setButtonTwo({
backgroundColor: "red",
border: "red"
})
}
return (
<div>
<Button style={{backgroundColor: buttonOne, border: buttonOne}} className="one" type="primary">First</Button>
<Button style={{backgroundColor: buttonTwo, border: buttonTwo}} onClick={buttonTwoBackgroundColor} className="two" type="primary">Second</Button>
</div>
)
}
export default App
This is App.css
.one {
margin-right: 5px;
margin-left: 250px;
margin-top: 50px;
}
.two, .three, .four, .five {
margin-right: 5px;
}
In the function buttonTwoBackgroundColor you set setButtonTwo to a object. it should be a string.
const buttonTwoBackgroundColor = () => {
setButtonTwo("red")
}
change
onClick={buttonTwoBackgroundColor}
to
onClick={setButtonTwo("red")}
you are using useState as string at initialization and assigning an object in function below, which is not proper way!
You are trying to set state to be an object when you have defined the defualt state as a string.
Update the default state to an object and then access properties like this:
import React, { useState } from "react";
import { Button } from "antd";
import "./styles.css";
export default function App() {
const [buttonOne, setButtonOne] = useState("red");
const [buttonTwo, setButtonTwo] = useState({backgroundColor: "blue", border: "blue"});
const buttonTwoBackgroundColor = () => {
setButtonTwo(prevState => ({
...prevState,
backgroundColor: 'blue',
border: 'red'
}));
};
return (
<div>
<Button
style={{ backgroundColor: buttonOne, border: buttonOne }}
className="one"
type="primary"
>
First
</Button>
<Button
style={{ backgroundColor: buttonTwo.backgroundColor, border: buttonTwo.border }}
onClick={buttonTwoBackgroundColor}
className="two"
type="primary"
>
Second
</Button>
</div>
);
}

CSSTransition from react-transition-group not applying classes

I'm trying to integrate CSSTransition to my Gatsby site, but it is not applying any of the classes. I'm utilizing CSS modules, and I've got a <div> that serves as the parent that fades in and out, essentially applying the fade effect to this and covering the content while it changes. It's got the class fadEffect. Here is my app-layout component, and the SASS.
AppLayout.tsx
import React, { ReactNode, useState } from 'react';
import { ApiContext } from 'contexts/ApiContext';
import { graphql, StaticQuery } from 'gatsby';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import { Devtools } from '../devtools/Devtools';
import { Footer } from '../footer/Footer';
import { Header } from '../header/Header';
import s from './AppLayout.scss';
interface AppLayoutProps {
children: ReactNode;
location: string;
}
const isDev = process.env.NODE_ENV === 'development';
// tslint:disable no-default-export
export default ({ children, location }: AppLayoutProps) => {
const [fadeEffectVisible, setFadeEffectVisible] = useState(false);
const handleFadeEffectEntered = () => {
setTimeout(() => {
setFadeEffectVisible(false);
}, 50);
};
return (
<StaticQuery
query={`${NavQuery}`}
render={(data) => (
<>
<ApiContext>
<Header navigationContent={data.prismic.allNavigations.edges[0].node} />
<CSSTransition
in={fadeEffectVisible}
timeout={150}
classNames={{
enter: s.fadeEffectEnter,
enterActive: s.fadeEffectEnterActive,
enterDone: s.fadeEffectEnterDone,
exit: s.fadeEffectExit,
exitActive: s.fadeEffectExitActive,
}}
onEntered={handleFadeEffectEntered}
>
<div className={s.fadeEffect} aria-hidden="true" />
</CSSTransition>
<TransitionGroup component={null}>
<CSSTransition
key={location}
timeout={150}
classNames={{
enter: s.pageEnter,
}}
>
<div className={s.layout}>
{children}
<Footer navigationItems={data.prismic.allNavigations.edges[0].node} />
{isDev && <Devtools />}
</div>
</CSSTransition>
</TransitionGroup>
</ApiContext>
</>
)}
/>
);
};
const NavQuery = graphql`
query NavQuery {
prismic {
allNavigations {
edges {
node {
...NotificationBar
...NavigationItems
...FooterNavigationItems
}
}
}
}
}
`;
AppLayout.scss
#import '~styles/config';
:global {
#import '~styles/base';
}
.layout {
display: block;
min-height: 100vh;
}
.pageEnter {
display: none;
}
.fadeEffect {
display: none;
position: fixed;
z-index: 9;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #fff;
transition: opacity 0.15s linear;
&Enter {
display: block;
opacity: 0;
}
&Active,
&Done,
&Exit {
display: block;
opacity: 1;
}
&ExitActive {
opacity: 0;
}
}
I'm happy to provide more details/code if this isn't enough. I'm newish to React and Gatsby, so I'm still learning the lingo. Thanks in advance.
I don't see part of your code where you are updating fadeEffectVisible to true for first CSSTransition and I don't see in property at all on second CSSTransition and I would bet that is your issue. Please take a look at this example from React Transition Group for understanding usage of properties.
App.js
function App() {
const [inProp, setInProp] = useState(false);
return (
<div>
<CSSTransition in={inProp} timeout={200} classNames="my-node">
<div>
{"I'll receive my-node-* classes"}
</div>
</CSSTransition>
<button type="button" onClick={() => setInProp(true)}>
Click to Enter
</button>
</div>
);
}
Style.css
.my-node-enter {
opacity: 0;
}
.my-node-enter-active {
opacity: 1;
transition: opacity 200ms;
}
.my-node-exit {
opacity: 1;
}
.my-node-exit-active {
opacity: 0;
transition: opacity 200ms;
}
When the in prop is set to true, the child component will first receive the class example-enter, then the example-enter-active will be added in the next tick.

Show all variations of a component on a single page in React Storybook but still have Knobs?

I have a button component that can be of different types eg primary, secondary, etc:
export const buttonTypes = [
'primary',
'secondary',
'tertiary',
'positive',
'negative',
]
const Button = ({ text, type }) => {
return(
<button className={type}>{text}</button>
)
}
Button.propTypes = {
text: PropTypes.string,
type: PropTypes.oneOf(buttonTypes),
}
In my Storybook file I'm mapping through the options. This means you can see all the variants on a single page and if another string was added to the buttonTypes array it would automatically be added to the style guide:
import ButtonComponent, { buttonTypes } from './Button';
const Button = () => {
return(
<div>
{
buttonTypes.map(type=>(
<ButtonComponent key={type} text={type} type={type} />
))
}
</div>
)
}
export default {
title: 'Components',
component: Button,
};
The problem is that this doens't work with with many of the add-ons eg knobs. For knobs to work you need Button to be the actual component, not a wrapper as I did above.
import ButtonComponent, { buttonTypes } from './Button';
const Button = () => {
return (
<ButtonComponent
type={select('type', buttonTypes, buttonTypes.primary)}
text="Button"
/>
);
};
Is there a way to use knobs and show all the variations on a single page? Ideally without having to create each component manually as this is more work and then won't automatically update if a new string is added to buttonTypes.
Use the grouping feature of knobs, that way each instance of your component will get its own knob instance instead of all the knob instances being shared between all of the component instances. You can even mix grouped knobs with non-grouped nobs if you want certain things to be shared and other not to be.
In the following example, I have a <Button/> story where each instance has its own copy of the type and disabled properties, but the text is shared between them all.
Each button type gets its own panel where you can set its type and disabled. The "Other" group contains any knobs that didn't have their group set (such as text).
src/Button/Button.component.jsx
import * as React from "react";
import "./Button.style.css";
export const Button = ({
text,
type,
disabled,
onClick
}) => (
<button
className={`button button--${type} ${disabled ? "button--disabled" : ""}`}
disabled={disabled}
onClick={onClick}
children={text}
/>
);
src/Button/Button.stories.jsx
import * as React from "react";
import {withKnobs, text, boolean, select} from "#storybook/addon-knobs";
import {action} from "#storybook/addon-actions";
import {Button, buttonTypes} from "./";
export default {
title: "Button",
component: Button,
decorators: [withKnobs]
};
export const ButtonStory = () => {
const buttontext = text("Text", "Click Me");
return (
<div>
{buttonTypes.map(buttonType => (
<div key={buttonType}>
<Button
type={select("Type", buttonTypes, buttonType, buttonType)}
disabled={boolean("Disabled", false, buttonType)}
onClick={action(`${buttonType} clicked`)}
text={buttontext}
/>
</div>
))}
</div>
);
};
ButtonStory.story = {
name: "All"
}
src/Button/Button.types.js
export const buttonTypes = [
"primary",
"secondary",
"tertiary"
];
src/Button/Button.style.css
.button {
padding: 0.5em;
font-size: 1.25em;
border-radius: 10px;
border-width: 2px;
border-style: solid;
border-color: black;
}
.button--primary {
background-color: rgb(132, 198, 106);
color: black;
border-color: black;
}
.button--secondary {
background-color: rgb(194, 194, 194);
color: black;
border-color: black;
}
.button--tertiary {
background-color: transparent;
color: inherit;
border-color: transparent;
}
.button--disabled {
background-color: rgb(194, 194, 194);
color: rgb(105, 102, 102);
border-color: rgb(105, 102, 102);
}
src/Button/index.js
export {Button} from "./Button.component";
export {buttonTypes} from "./Button.types";

React Hooks Drawer Menu not Showing CSS Transition

My Menu Drawer is working except for the css transitions. I think whta is happening is, when I change the value of menuOpen (which is a useState), the DOM rerenders and the transition never happens. How do I stop this? I think I need to use the useRef I have already, but not sure how?
My Page Component with a white div that will be the drawer:
import React, { useState, useEffect, useRef } from 'react';
import { Typography } from '#material-ui/core';
import './page.css';
function Page({ someProps }) {
const [ menuOpen, setMenuOpen ] = useState(false);
const menuRef = useRef();
const handleMenuClick = () => {
setMenuOpen(!menuOpen);
console.log('MENU CLICKED!!!!!!!!!!!!!!!!!!!!', menuOpen);
};
const handleClickOutside = (event) => {
console.log('CLICKED!!!!!!!!!!!!!!!!!!!!', event, menuRef.current);
if (menuRef.current && !menuRef.current.contains(event.target) && menuOpen === true) {
setMenuOpen(false);
}
};
useEffect(
() => {
document.addEventListener('click', handleClickOutside, false);
return () => {
document.removeEventListener('click', handleClickOutside, false);
};
},
[ menuOpen ]
);
return (
<Typography className="screen">
<div className="menuButton" onClick={handleMenuClick}>
MENU
</div>
{menuOpen && <div ref={menuRef} className={`menuContainer ${menuOpen === true ? 'isOpen' : ''}`} />}
</Typography>
);
}
export default Page;
My page.css:
.menuContainer {
position: fixed;
top: 0;
left: 0;
width: 250px;
height: 100vh;
background-color: white;
z-index: 1;
transition: margin 1s ease-in;
margin: 0 0 0 -250px;
}
.menuContainer.isOpen {
margin: 0 0 0 0px;
transition: margin 2s;
}

Resources