ReactJs adding active class to DIV - reactjs

I have 3 DIVs, dynamically created. My target is: when any div is clicked to add active class to it, and of course if any other has that active class to remove it. How can I achieve that?
import React, { useState } from "react";
import "./styles/App.css";
import { Container, Box, Typography, makeStyles } from "#material-ui/core";
const useStyles = makeStyles({
mainBox: {
display: "flex",
justifyContent: "space-evenly"
},
mybox: {
backgroundColor: "#9fa8da",
padding: "40px",
color: "#fff",
maxWidth: 300
}
});
function App() {
const clasess = useStyles();
const [active, setactive] = useState("");
const mydata = [
{
id: 1,
name: "Ganesh",
des: "UI Developer"
},
{
id: 2,
name: "Suresh",
des: "Accountant"
},
{
id: 3,
name: "Srikanth",
des: "Digital"
}
];
const onClick = index => {
setactive("active");
};
return (
<Container>
<Box className={clasess.mainBox}>
{mydata.map((val, index) => {
return (
<>
<Box
boxShadow={1}
key={index}
onClick={e => {
onClick(index);
}}
className={active}
>
<Typography variant="h4">{val.name}</Typography>
<Typography>{val.des}</Typography>
</Box>
</>
);
})}
</Box>
</Container>
);
}
export default App;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
I have 3 DIVs, dynamically created. My target is: when any div is clicked to add active class to it, and of course if any other has that active class to remove it. How can I achieve that?
I have 3 DIVs, dynamically created. My target is: when any div is clicked to add active class to it, and of course if any other has that active class to remove it. How can I achieve that?

Try to check when active id is the clicked id:
import React, { useState } from "react";
import "./styles/App.css";
import { Container, Box, Typography, makeStyles } from "#material-ui/core";
const useStyles = makeStyles({
mainBox: {
display: "flex",
justifyContent: "space-evenly"
},
mybox: {
backgroundColor: "#9fa8da",
padding: "40px",
color: "#fff",
maxWidth: 300
}
});
function App() {
const clasess = useStyles();
const [active, setActive] = useState();
const mydata = [
{
id: 1,
name: "Ganesh",
des: "UI Developer"
},
{
id: 2,
name: "Suresh",
des: "Accountant"
},
{
id: 3,
name: "Srikanth",
des: "Digital"
}
];
const onClick = id => {
setActive(id);
};
return (
<Container>
<Box className={clasess.mainBox}>
{mydata.map((val, index) => {
return (
<>
<Box
boxShadow={1}
key={index}
onClick={e => {
onClick(val.id);
}}
className={val.id == id ? active : deactive}
>
<Typography variant="h4">{val.name}</Typography>
<Typography>{val.des}</Typography>
</Box>
</>
);
})}
</Box>
</Container>
);
}
export default App;

You already managed on your Box component to add an "active" classname when one of them is Clicked.
But, you need to add some style or something to show for those "active" elements.
Inside of useStyle add the class active and test it out with some styling:
i.e)
const useStyles = makeStyles({
mainBox: {
display: "flex",
justifyContent: "space-evenly"
},
mybox: {
backgroundColor: "#9fa8da",
padding: "40px",
color: "#fff",
maxWidth: 300
},
active {
backgroundColor: "red"
}
});
I'm not sure if you need to add "active" class using classes, something like :
<Box
boxShadow={1}
key={index}
onClick={e => {
onClick(index);
}}
className={classes.active}
>
<Typography variant="h4">{val.name}</Typography>
<Typography>{val.des}</Typography>
</Box>

Related

React state from props that doesn't update (even doing destructuring and using different keys)

Sorry for the long question in advance but I have been struggling with this for some time
To give some context of what I am trying to accomplish I am loading this table when my app loads
And when I click the button inside red square will bring the user to edit mode
In edit mode user should be able to for example toggle the High School Core (inside green square) checkbox and then discard the change by clicking Discard Changes button (inside blue square)
The thing is that it's not working, this checkbox should not be enabled because that Discard Changes button is only setting the editMode state to false and by doing that the table should be created by mapping other object: const whichCourse = editMode ? modifiedValues : originalValues
The original data from which originalValues and modifiedValues are created is passed to the <Table /> component as a prop (that's a requirement for this app) from App.tsx
import { v4 as uuidv4 } from "uuid";
import { Container } from "#mui/material";
import Table from "./Table";
const ID1 = uuidv4();
const ID2 = uuidv4();
const ID3 = uuidv4();
export const typedData = {
bundles: [
{
id: ID1,
name: "High School Core",
},
{
id: ID2,
name: "Middle School Core",
},
{
id: ID3,
name: "Elementary School Core",
},
],
schools: [
{
id: uuidv4(),
name: "First School",
licensedproducts: [ID1, ID2],
},
{
id: uuidv4(),
name: "Second School",
licensedproducts: [ID2, ID3],
},
],
};
export default function App() {
return (
<Container>
<Table propsData={typedData} />
</Container>
);
}
File Table.tsx contains the following to render the UI and handle all logic
import { useState, useEffect } from "react";
import CheckIcon from "#mui/icons-material/Check";
import { Box, Grid, Button, Checkbox } from "#mui/material";
import { typedData } from "./App";
const tableStyles = {
display: "block",
overflowX: "auto",
paddingTop: "36px",
whiteSpace: "nowrap",
fontFamily: "Helvetica Neue",
"& table": {
width: "100%",
textAlign: "center",
borderCollapse: "collapse",
},
"& th, td": {
px: "17px",
color: "#1E1E24",
fontSize: "14px",
fontWeight: "400",
lineHeight: "40px",
},
"& th": {
borderBottom: "2px solid #00006D",
},
"& td": {
borderBottom: "1px solid #dddddd",
},
"& th:nth-of-type(1), td:nth-of-type(1), th:nth-of-type(2), td:nth-of-type(2)": {
textAlign: "left",
},
};
export default function Table({ propsData }: { propsData: typeof typedData }) {
const [editMode, setEditMode] = useState(false);
const [originalValues, setOriginalValues] = useState({ ...propsData });
const [modifiedValues, setModifiedValues] = useState({ ...propsData });
useEffect(() => {
console.log("running useEffect");
setOriginalValues({ ...propsData });
setModifiedValues({ ...propsData });
}, [propsData]);
const whichCourse = editMode ? modifiedValues : originalValues;
const keyComplement = editMode ? "yes" : "not";
const toggleEdit = () => {
setEditMode((current) => !current);
};
const saveButton = () => {
setOriginalValues(modifiedValues);
};
return (
<Box sx={{ textAlign: "center", pt: "10px" }}>
<Grid container spacing={2}>
<Grid item xs>
<Button variant="contained" onClick={toggleEdit}>
{editMode ? "Discard changes" : `Edit Mode - ${keyComplement}`}
</Button>
</Grid>
{editMode && (
<Grid item xs>
<Button variant="contained" onClick={saveButton}>
Save changes
</Button>
</Grid>
)}
</Grid>
<Box sx={tableStyles}>
<Box component="table" sx={{ overflowX: "auto" }} tabIndex={0}>
<thead>
<tr>
<Box component="th">ID</Box>
<Box component="th">School Name</Box>
{whichCourse.bundles.map((thisBundle) => {
return (
<Box component="th" key={`th-${thisBundle.id}-${keyComplement}`}>
{thisBundle.name}
</Box>
);
})}
</tr>
</thead>
<tbody>
{whichCourse.schools.map((thisSchool, currentIndex) => {
return (
<tr key={`td-${thisSchool.id}-${keyComplement}`}>
<Box component="td">{thisSchool.id}</Box>
<Box component="td">{thisSchool.name}</Box>
{whichCourse.bundles.map((thisBundle) => {
const isEnabled = thisSchool.licensedproducts.includes(thisBundle.id);
return (
<Box component="td" key={`td-${thisBundle.id}-${keyComplement}`}>
{editMode ? (
<Checkbox
size="small"
checked={isEnabled}
sx={{
color: "#000000",
"&.Mui-checked": {
color: "#3F51B5",
},
}}
onChange={() =>
setModifiedValues((currentValue) => {
if (isEnabled) {
currentValue.schools[currentIndex].licensedproducts = currentValue.schools[currentIndex].licensedproducts.filter((value) => value !== thisBundle.id);
} else {
currentValue.schools[currentIndex].licensedproducts.push(thisBundle.id);
}
return { ...currentValue };
})
}
/>
) : (
isEnabled && <CheckIcon sx={{ verticalAlign: "middle" }} />
)}
</Box>
);
})}
</tr>
);
})}
</tbody>
</Box>
</Box>
</Box>
);
}
I created a very simple repo with this code and a CloudFlare Pages deploy
GitHub repo: https://github.com/LuisEnMarroquin/table-toggles-discard
Live preview: https://toggle.pages.dev/
Problem with your code is this:
const [originalValues, setOriginalValues] = useState({ ...propsData });
const [modifiedValues, setModifiedValues] = useState({ ...propsData });
You just spread same object in two different states, by spread you shallowly copied only top level of object, but all other nested things are shared between originalvalues and modifiedvalues since those nested references were not copied.
And like that each time you check unchecked checkbox, like this:
else {
currentValue.schools[currentIndex].licensedproducts.push(thisBundle.id);
}
You are basically modifying both original and modified values, since you just pushed in that same licencesedproducts array that was shared because you were not copied deeply when setting initial state.
You should either do deep copy on initial state set(which I suggest), or/and in this part where you modify state to use spread of array in order to create new one, eg: currentValue.schools[currentIndex].licensedproducts = [...currentValue.schools[currentIndex].licensedproducts, thisBundle.id]
const [originalValues, setOriginalValues] = useState({...propsData });
const [modifiedValues, setModifiedValues] = useState({ ...propsData });
currentValue.schools[currentIndex].licensedproducts = currentValue.schools[currentIndex].licensedproducts.filter((value) => value !== thisBundle.id);
currentValue.schools[currentIndex].licensedproducts.push(thisBundle.id);
The issue was that you were using the same object to initialize both the states though you are spreading the object but that object also have inner objects. So those inner objects bundles and schools in both the states point to a single object in memory.
And in the onChange handler you are mutating the object as a result it updates both the state values.
It looks like you need to apply another logic to set isEnabled. I do not know
your business logic, however the most simple solution will be to use ! operator:
const isEnabled = !thisSchool.licensedproducts.includes(thisBundle.id);

Change the text with specific checkbox is clicked

I have an array of object. I want to change the text to available when checkbox is clicked and unavailable when checkbox is not clicked. The problem I am facing is when I select one item, it changes all the text to the available. How can I change the particular item text that is checked?
import "./styles.css";
import React, { useState } from "react";
const weekSchedule = [
{ day: "Sun", id: 0 },
{ day: "Mon", id: 2 }
];
export default function App() {
const [checkbox, setCheckbox] = useState(false);
const handleClick = (e) => {
setCheckbox(!checkbox);
};
return (
<div className="App">
{weekSchedule.map((day, i) => (
<div
key={i}
style={{
display: "flex",
flexDirection: "row",
justifyContent: "center",
alignItems: "center"
}}
>
<input type="checkbox" checkbox={checkbox} onClick={handleClick} />
<h1>{day.day}</h1>`**enter code here**`
<h2>{checkbox ? "Available" : "Unavailable"}</h2>
</div>
))}
</div>
);
}
You would want to make the checkboxes controlled components: https://reactjs.org/docs/forms.html#controlled-components. Then we can use the index from the map to target changes in state.
import "./styles.css";
import React, { useState } from "react";
const weekSchedule = [
{ day: "Sun", id: 0, checked:false },
{ day: "Mon", id: 2, checked:false }
];
export default function App() {
const [checkbox, setCheckbox] = useState(weekSchedule);
const handleClick = (e,i) => {
const newWeekSchedule = [...checkbox];
newWeekSchedule[i].checked = !newWeekSchedule[i].checked;
setCheckbox(newWeekSchedule);
};
return (
<div className="App">
{checkbox.map((day, i) => (
<div
key={i}
style={{
display: "flex",
flexDirection: "row",
justifyContent: "center",
alignItems: "center"
}}
>
<input value={checkbox[i].checked} type="checkbox" checkbox={checkbox} onClick={(e)=> handleClick(e,i)} />
<h1>{day.day}</h1>`**enter code here**`
<h2>{day.checked ? "Available" : "Unavailable"}</h2>
</div>
))}
</div>
);
}
You have only one state for checkbox. You're mapping all the elements in weekSchedule to one checkbox. If you want them to be separate, you need to keep track of them separately.
import "./styles.css";
import React, { useState } from "react";
export default function App() {
const [weekSchedule, setWeekSchedule] = useState([
{ day: "Sun", id: 0, checked: false },
{ day: "Mon", id: 2, checked: false}
]);
const handleClick = (e,id) => {
let newWeeklySchedArray = [...weekSchedule]
newWeeklySchedArray[id].checked = !e.target.checked;
setWeekSchedule(newWeeklySchedArray)
};
return (
<div className="App">
{weekSchedule.map((day, i) => (
<div
key={i}
style={{
display: "flex",
flexDirection: "row",
justifyContent: "center",
alignItems: "center"
}}
>
<input type="checkbox" onChange={(e)=>{handleClick(e,i)}} />
<h1>{day.day}</h1>`**enter code here**`
<h2>{!weekSchedule[i].checked ? "Available" : "Unavailable"}</h2>
</div>
))}
</div>
);
}
Here's a link for the demo above :)
https://codesandbox.io/s/nostalgic-jones-u11ux?file=/src/App.js

Dynamic URL in React

I'm working on a React project with Redux and I'm consuming a Rest API, I need to implement a functionality where when I select a project from a list and I need to load the project ID in the URL and direct to another screen where a sidebar with the options is loaded. navigation of this project.
Example: Layout
I managed to load the project's Id in the URL and retrieve this ID in the project's home screen, the problem is to store the project's Id and set this ID in the next selected URLs, for example:
path: '/project/:id/companies'
path: '/project/:id/currencies'
path: '/project/:id/settings'
List of projects:
Capture the project id and arrow the url:
href={`#/project/${row.id}/main`}
Routes:
path: '/project/:id/main',
exact: true,
name: 'projectMain',
component: RequireAuth(ProjectMain),
Retrieve ID in main
import { useParams } from 'react-router-dom';
...
const { id } = useParams();
The problem is in the sidebar, where I load a list of items with the path, I'm not able to pass the project id in this list.
Complementando a pergunta
In Sidebar I'm using useHistory(), the problem is that the path comes static by 'props' through importing a file into my template, as you can see below:
Template
import React from 'react';
import { Grid, makeStyles } from '#material-ui/core';
import {
AppContent,
AppHeader,
SidebarApp,
} from '../components/index';
import itemsProject from '../components/itemsSidebar/itemsProject';
const useStyles = makeStyles(theme => ({
appContent: {
paddingLeft: 240,
width: '100%',
backgroundColor: theme.palette.background.paper,
},
}));
const ProjectLayout = () => {
const classes = useStyles();
return (
<div className={classes.appContent}>
<AppHeader />
<Grid container direction="row">
<SidebarApp items={itemsProject} />
<AppContent />
</Grid>
</div>
);
};
export default ProjectLayout;
Sidebar:
/* eslint-disable react/jsx-no-duplicate-props */
import React from 'react';
import List from '#material-ui/core/List';
import ListItem from '#material-ui/core/ListItem';
import Divider from '#material-ui/core/Divider';
import ExpandMoreIcon from '#material-ui/icons/ExpandMore';
import ExpandLessIcon from '#material-ui/icons/ExpandLess';
import Collapse from '#material-ui/core/Collapse';
import {
alpha,
Box,
Card,
ListSubheader,
makeStyles,
Typography,
} from '#material-ui/core';
import { useHistory, useLocation, useParams } from 'react-router-dom';
import translate from '../providers/i18n/translate';
const useStyles = makeStyles(theme => ({
sidebar: {
background: theme.palette.background.dark,
width: 240,
height: '100vh',
border: '1px solid rgba(0, 0, 0, 0.1)',
display: 'flex',
flexDirection: 'column',
position: 'absolute',
paddingTop: 64,
top: 0,
left: 0,
},
sidebarItem: {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
sidebarItemContent: {
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
display: 'flex',
alignItems: 'center',
width: '100%',
},
sidebarItemIcon: {
marginRight: 6,
},
sidebarItemText: {
width: '100%',
},
sidebarItemExpandArrow: {
fontSize: '1.2rem !important',
},
sidebarItemExpandArrowExpanded: {
fontSize: '1.2rem !important',
color: theme.palette.primary.main,
fontWeight: 'bold',
},
active: {
background: alpha(theme.palette.primary.light, 0.2),
},
}));
function SidebarItem({ depthStep = 10, depth = 0, expanded, item, ...rest }) {
const [collapsed, setCollapsed] = React.useState(true);
const { label, items, Icon, onClick: onClickProp } = item;
const classes = useStyles();
const history = useHistory();
const location = useLocation();
function toggleCollapse() {
setCollapsed(prevValue => !prevValue);
}
function onClick(e) {
if (Array.isArray(items)) {
toggleCollapse();
}
if (onClickProp) {
onClickProp(e, item);
history.push(item.path);
}
}
let expandIcon;
if (Array.isArray(items) && items.length) {
expandIcon = !collapsed ? (
<>
<ExpandLessIcon className={classes.sidebarItemExpandArrowExpanded} />
</>
) : (
<ExpandMoreIcon className={classes.sidebarItemExpandArrow} />
);
}
return (
<>
<ListItem
className={classes.sidebarItem}
onClick={onClick}
button
dense
className={location.pathname === item.path ? classes.active : null}
{...rest}
>
<div
style={{ paddingLeft: depth * depthStep }}
className={classes.sidebarItemContent}
>
{Icon && (
<Icon
className={classes.sidebarItemIcon}
fontSize="small"
color="primary"
/>
)}
<div className={classes.sidebarItemText}>{label}</div>
</div>
{expandIcon}
</ListItem>
<Collapse in={!collapsed} timeout="auto" unmountOnExit>
{Array.isArray(items) ? (
<List disablePadding dense>
{items.map((subItem, index) => (
<React.Fragment key={`${subItem.name}${index}`}>
{subItem === 'divider' ? (
<Divider style={{ margin: '6px 0' }} />
) : (
<SidebarItem
depth={depth + 1}
depthStep={depthStep}
item={subItem}
/>
)}
</React.Fragment>
))}
</List>
) : null}
</Collapse>
</>
);
}
function Sidebar({ items, depthStep, depth, expanded }) {
const classes = useStyles();
const { key } = useParams();
return (
<Card elevation={0} className={classes.sidebar}>
<List
disablePadding
dense
subheader={
<ListSubheader component="div" id="nested-list-subheader">
{translate('sidebarMenuSettings')}
<Typography>
<Box>{key}</Box>
</Typography>
</ListSubheader>
}
>
{items.map((sidebarItem, index) => (
<React.Fragment key={`${sidebarItem.name}${index}`}>
{sidebarItem === 'divider' ? (
<Divider style={{ margin: '6px 0' }} />
) : (
<SidebarItem
depthStep={depthStep}
depth={depth}
expanded={expanded}
item={sidebarItem}
/>
)}
</React.Fragment>
))}
</List>
</Card>
);
}
export default Sidebar;
Sidebar list items
function onClick(e, item) {}
const itemsProject = [
{
name: 'companies',
label: translate('sidebarProjectCompanies'),
Icon: CompanyIcon,
path: '/project/:id/companies',
onClick,
}
{
name: 'currencies',
label: translate('sidebarProjectCurrencies'),
Icon: CurrencyIcon,
path: '/project/:id/currencies',
onClick,
}
];
export default itemsProject;
How can I pass the ID variable on the Sidebar list items?
I thank you for your help!
You can use ES6 template literals as follows.
path: `/project/${id}/companies`
Since you already defined your path, you just need to use useHistory and navigate to the new link
import { useHistory } from 'react-router';
...
const history = useHistory();
...
// call this whenever you want to navigate
history.push(`/project/${id}/currencies`);

Material UI pagination - How Can I use custom style for number of pages?

I'm quite new to material-ui. I'm trying to build this component.
I was able to do the style for the next and previous buttons the same as in the picture.
The normal style shows the number of pages as a numbered group besides each other like this:
Are there any properties that I can pass for the pagination component, in which I can change the style?
Here is the code:
import Pagination from "#material-ui/lab/Pagination";
import useStyles from "./styles";
const ReviewsPagination = () => {
const classes = useStyles();
return (
<div className={classes.root}>
<Pagination count={8} />
</div>
);
};
export default ReviewsPagination;
and the style file:
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles({
root: {
"& .MuiPagination-ul": {
"& > li:first-child": {
"& button": {
borderRadius: "50%",
border: "1px solid black",
width: "48px",
height: "48px",
},
},
"& > li:last-child": {
"& button": {
borderRadius: "50%",
border: "1px solid black",
width: "48px",
height: "48px",
},
},
},
},
});
export default useStyles;
Thank you!
you can use the usePagination hook to customize the pagination component. Like below:
export default function UsePagination() {
const classes = useStyles();
const { items } = usePagination({
count: 10,
});
return (
<nav>
<ul className={classes.ul}>
{items.map(({ page, type, selected, ...item }, index) => {
let children = null;
if (type === 'next' || type === 'previous') {
children = (
<button type="button" {...item}>
{type}
</button>
);
}else if(selected){
children = <div>{`${page}/10`}</div>
}
return <li key={index}>{children}</li>;
})}
</ul>
</nav>
);
}

how to increment value by +1 by clicking button in react js

am stuck with a problem when I click on like button I want it increment by +1, can anyone tell me how can I do it, I try it but I can't solve it.
below I share my all code that I used to make my small application. if you have question free feel to ask me.
Music.js
This is my music form where I wrote my whole code.
import React, { useState, useRef, useEffect } from 'react';
import { makeStyles } from '#material-ui/core/styles';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import Button from '#material-ui/core/Button';
import IconButton from '#material-ui/core/IconButton';
import MenuIcon from '#material-ui/icons/Menu';
import ExitToAppIcon from '#material-ui/icons/ExitToApp';
import RadioIcon from '#material-ui/icons/Radio';
import firebase from '../Firebase';
import SearchBar from "material-ui-search-bar";
import { useHistory } from 'react-router-dom';
import { Container, Input, TextField } from '#material-ui/core';
import './Music.css';
import Table from '#material-ui/core/Table';
import TableBody from '#material-ui/core/TableBody';
import TableCell from '#material-ui/core/TableCell';
import TableContainer from '#material-ui/core/TableContainer';
import TableHead from '#material-ui/core/TableHead';
import TableRow from '#material-ui/core/TableRow';
import Paper from '#material-ui/core/Paper';
import ThumbUpAltIcon from '#material-ui/icons/ThumbUpAlt';
// import { useSelector } from 'react-redux';
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
},
menuButton: {
marginRight: theme.spacing(2),
},
title: {
flexGrow: 1,
},
search: {
marginTop: 30,
},
table: {
minWidth: 650,
marginBottom: 10,
},
cells: {
marginTop: 50,
},
thumb: {
color: '#e75480',
},
name: {
color: 'blue',
padding: 10,
fontSize: 20,
},
audios: {
display: 'flex',
margin: 'auto',
height: 50,
width: 500,
alignItems: 'center'
},
icon: {
fontSize: 40,
color: '#e75480',
cursor:'pointer'
}
}));
const Music = () => {
const history = useHistory();
const inputRef = useRef(null);
const [search, setSearch] = useState("");
const [like, setLike] = useState(false);
const [music,setMusics] = useState([
{
id:"1",
name:"Arijit singh",
audiosrc:"https://upload.wikimedia.org/wikipedia/commons/1/15/Bicycle-bell.wav",
},
{
id:"2",
name:"Atif Aslam",
audiosrc:"https://upload.wikimedia.org/wikipedia/commons/1/15/Bicycle-bell.wav",
},
{
id:"3",
name:"Sonu Nigam",
audiosrc:"https://upload.wikimedia.org/wikipedia/commons/1/15/Bicycle-bell.wav",
},
{
id:"4",
name:"Neha kakkar",
audiosrc:"https://upload.wikimedia.org/wikipedia/commons/1/15/Bicycle-bell.wav",
},
])
const likeButton = (id)=>{
const likebuttons = music.find((curElem)=>{
return curElem.id == id
})
setLike({likebuttons:like+1})
console.log(likebuttons);
}
console.log(search);
// Logout Functionality
const Logout = () => {
firebase.auth().signOut().then(() => {
alert("Logout Successfull...");
history.push('/');
}).catch((error) => {
console.log(error);
});
}
const classes = useStyles();
return (
<div className={classes.root}>
<AppBar position="static" style={{ width: '100%' }}>
<Toolbar>
<IconButton edge="start" className={classes.menuButton} color="inherit" aria-label="menu">
<RadioIcon style={{ fontSize: "30px" }} /> React Music
</IconButton>
<Typography variant="h6" className={classes.title}>
</Typography>
<Button color="inherit" onClick={Logout}> <ExitToAppIcon /> Logout </Button>
</Toolbar>
</AppBar>
<Container>
<SearchBar className={classes.search}
value={search}
onChange={(newValue) => setSearch(newValue)}
/>
<Table className={classes.table} aria-label="simple table">
<TableBody>
{music && music.map(mus=>{
return(
<TableRow>
<TableCell style={{ display: 'flex', justifyContent: 'space-around' }}>
<div>
<h3>{like} <ThumbUpAltIcon className={classes.icon} onClick={()=>likeButton(music.id)} /> {mus.name}</h3>
</div>
<div>
<audio ref={inputRef} src={mus.audiosrc} className={classes.audios} controls />
</div>
</TableCell>
</TableRow>
)
})}
</TableBody>
</Table>
</Container>
</div>
);
}
export default Music;
Make following changes in your code:
const [like, setLike] = useState(false);
to
const [like, setLike] = useState(0);
and
setLike({likebuttons:like+1})
to
setLike(like+1);
and try again.
If you want to have like count for all music data,
First you should change like state to store object
const [like, setLike] = useState({});
Change your like button function to store data in object
const likeButton = (musicId)=>{
setLike({...like, like[musicId]: like[musicId] + 1 || 1 })
}
Now change how you are showing like count as below:
<div>
<h3>{like[music.id]} <ThumbUpAltIcon className={classes.icon} onClick={()=>likeButton(music.id)} /> {mus.name}</h3>
</div>
first, you should change your states.
each music has it's own like number, so you can't use 1 like state for all.
each item in your music array should have a value to store it's own like
numbers.
something like :
const [music,setMusics] = useState([
{
id:"1",
name:"Arijit singh",
audiosrc:"https://uploa...",
likeNumbers:0,
},
...
]
then your onClick should iterate the music array, find the clicked item and change it's likeNumbers value.
something like this:
cosnt handleClick = (id) => {
setMusic((prevState) => prevState.map((item) => {
if (item.id === id ) {
return {...item, likeNumbers:likeNumbers + 1 }
} else {
return item
}
})
}
to be clear:
first you should take the last version of your state, then in the process of iterating, find the clicked item and update it. to avoid mutate your state ( because items are objects ), you should make a copy first, then changed it. I mean here:
return {...item, likeNumbers:likeNumbers + 1 }
after all a part of your jsx code should be changed to :
<h3>{mus.likeNumber} <ThumbUpAltIcon className={classes.icon} onClick={()=>likeButton(music.id)} /> {mus.name}</h3>
First you need to make type as number instead of boolean
const [like, setLike] = useState(0);
and then
const likeButton = (id)=>{
const likebuttons = music.find((curElem)=>{
return curElem.id == id
})
setLike(likebuttons+1);
console.log(likebuttons);
}
Or
you can add no. of likes in same array object and just update that like field
You can check below example created for you. I hope it will help you.
It's having only like functionality
import "./styles.css";
import React, { useState } from "react";
export default function App() {
const [likedSong, setLikedSong] = useState({
likes: 0,
id: 0
});
const [music, setMusics] = useState([
{
id: "1",
name: "Arijit singh",
audiosrc:
"https://upload.wikimedia.org/wikipedia/commons/1/15/Bicycle-bell.wav",
likes: 0
},
{
id: "2",
name: "Atif Aslam",
audiosrc:
"https://upload.wikimedia.org/wikipedia/commons/1/15/Bicycle-bell.wav",
likes: 0
},
{
id: "3",
name: "Sonu Nigam",
audiosrc:
"https://upload.wikimedia.org/wikipedia/commons/1/15/Bicycle-bell.wav",
likes: 0
},
{
id: "4",
name: "Neha kakkar",
audiosrc:
"https://upload.wikimedia.org/wikipedia/commons/1/15/Bicycle-bell.wav",
likes: 0
}
]);
const handleLike = (list) => {
if (likedSong.id === list.id) {
setLikedSong({ ...likedSong, id: list.id, likes: ++likedSong.likes });
} else {
setLikedSong({ ...likedSong, id: list.id, likes: 0 });
}
};
return (
<div className="App">
{music.map((mus) => (
<div key={mus.id} className="music-list">
<div>
{mus.id === likedSong.id && (
<span className="no-likes">{likedSong.likes}</span>
)}
<span className="btn-like" onClick={() => handleLike(mus)}>
Like
</span>
{mus.name}
</div>
</div>
))}
</div>
);
}
Live working demo

Resources