React: Setting onClick method appropriately - reactjs

I am trying to update star icons in React on my profile cards so that when the user clicks the star it saves the profile to the users favourites. I only want it to be for loggedin users and otherwise i want a conditional render method so that the star isn't shown at all if the user is not logged in.
I am trying to figure out how to update the below code. There is already an onClick method in there but i think it doesn't need to be as we aren't using the font awesome icons for rating, just to save favourites. The current method for onClickDetail means that if anywhere in the profile card is clicked the user is directed to the underlying profile. I need to some how provide and exception that it doesn't apply to the star icon itself.
Thanks for your help.
import React, {useState} from 'react';
import {FontAwesomeIcon} from '#fortawesome/react-fontawesome';
import {faMapMarkerAlt} from '#fortawesome/free-solid-svg-icons';
import {ServiceType} from 'shared/models/profile/serviceType';
import {toTitleCase} from 'shared/utils/string';
import {FeeKind} from 'shared/models/fee-kind/FeeKind';
import {useAuth} from 'shared/hooks/useAuth';
import {ProfileCardFee} from 'shared/components/ProfileCard/ProfileCardFee/ProfileCardFee';
import {StarRatingStar} from 'shared/components/StarRating/StarRatingStar';
import './style.scss';
import {getFullName} from 'shared/utils/profile';
import {IProfile} from 'shared/models/profile/IProfile';
interface IProfileCardProps {
profile: IProfile;
onClickDetail?: () => void;
}
export const ProfileCardDetails = ({profile, onClickDetail}: IProfileCardProps) => {
const fullName = getFullName(profile);
const professionTitle = profile.profession ? toTitleCase(profile.profession) : null;
const [tempRating, setTempRating] = useState<number | undefined>(undefined);
const [ratingValue, setRatingValue] = useState<number>(-1);
const numStars: number = 1;
const {isAuthenticated, tokenData} = useAuth();
const onChangeStartIndex = (value: number) => {
if (ratingValue >= 0) {
setRatingValue(-1);
} else {
setRatingValue(value);
}
};
return (
<div onClick={onClickDetail} className="ProfileCard__details d-flex justify-content-between">
<div>
<div className="ProfileCard__profession">{professionTitle || 'undefined'}</div>
<div className="ProfileCard__title">{fullName}</div>
<div className="ProfileCard__location">
<FontAwesomeIcon icon={faMapMarkerAlt} className="ProfileCard__location-icon" />
{profile?.contact_details_display?.city}
</div>
</div>
<div className="flex_colum_end">
{/* {isAuthenticated ? ( */}
<div className="text_align_end ProfileCard__star_icon">
{[...Array(numStars)].map((_, starIndex) => (
<StarRatingStar
key={starIndex}
isClickable={true}
onClick={() => onChangeStartIndex(starIndex)}
onMouseEnter={() => setTempRating(starIndex)}
onMouseLeave={() => setTempRating(undefined)}
isActive={starIndex <= (ratingValue ?? -1)}
isHover={
tempRating !== undefined &&
starIndex <= tempRating &&
starIndex > (ratingValue ?? -1)
}
/>
))}
</div>
{/* )
: (
''
)} */}
<div
className={
profile && profile.service_types && profile.service_types.includes('FIXED_FEES' as ServiceType)
? 'flex_row_center mobileDayHour Hourly_rate_responsive'
: 'flex_center'
}
>
<div
className={
profile &&
profile.service_types &&
profile.service_types.includes('FIXED_FEES' as ServiceType)
? 'text_align_end'
: 'text_align_end'
}
>
<ProfileCardFee amount={profile.service_details?.hour_rate as number} kind={FeeKind.Hour} />
</div>
<div className="text_align_end pl-3">
<ProfileCardFee amount={profile.service_details?.day_rate as number} kind={FeeKind.Day} />
</div>
</div>
<div className="fixed_fee_enum_div">
{profile &&
profile.service_types &&
profile.service_types.includes('FIXED_FEES' as ServiceType) && (
<>
{' '}
<div className="fixed_fee_enum_svg">
<img
className="fixed_fee_enum_svg_icon"
src="/assets/profileIcons/lock.svg"
alt="Rightful Logo"
/>
</div>
<div className="fixed_fee_enum_text">Fixed Fee Options</div>
</>
)}{' '}
</div>
</div>
</div>
);
};
I think the onClick method needs to be something like this, updated so that it does not show a conditional render of the star icon rather than a toast if the user is not logged in.
const {tokenData} = useAuth();
const onClick = () => {
if (!tokenData) {
toast.error('Not Signed In', 'Please sign in to save profiles to your Talent List');
return;
}
(async () => {
const favorite = await createFavoriteAsync({
user_id: tokenData.id, // The ID of the current signed-in user
profile_id: profile.id, // The profile they are "starring"
});
})();
}

You can use stopPropagation to do it. Change your onClick method like the onClick below
<StarRatingStar
onClick={(e) => {
onChangeStartIndex(starIndex);
e.stopPropagation();
}
}
/>

Related

React change css style of a div in another component by button clicking in another component

on my Project I have a banner on top of my site with 2 buttons. when I click the button profile I want it to change the css style of a div in another component.
this is my code for the banner:
import Profile from "./Profile";
function Banner() {
const invis=false;
return (
<div className="banner">
<span className="bannerbtnsettings">
<button className="btnbannersettings">Settings</button>
</span>
<span className="bannerbtnprofile">
<button className="btnbannerprofile" onClick={Profile.changeStyle}>Profile</button>
</span>
</div>
);
}
export default Banner;
this is my code for the div in the other component:
import "../index.css";
import React, { useState } from "react";
const Profile = () => {
const [style, setStyle] = useState("profile-hidden");
const changeStyle = () => {
console.log("you just clicked");
setStyle("profile-displayed");
};
return (
<div>
<div className={style}> hellllo</div>
</div>
);
};
export default Profile;
I can only find information about this with parent-child components.
They said I should use a usestate import but I can't seem to get it working. what's the proper way to do this?
All you need is lift your state to parent component, if you have a long trip to your common ancestor you can try to use a context. Attached a working example. Hope it helps!
const Banner = ({ onClickHandler }) => {
return (
<div className="banner">
<span className="bannerbtnsettings">
<button className="btnbannersettings">Settings</button>
</span>
<span className="bannerbtnprofile">
<button className="btnbannerprofile" onClick={() => onClickHandler()}>Profile</button>
</span>
</div>
)}
const Profile = ({ style }) => {
return (
<div>
<div className={style}>I'm your profile :)</div>
</div>
);
};
const App = () => {
// We lift the state
const [style, setStyle] = React.useState("profile-hidden");
const profileHandler = () => {
setStyle(style === 'profile-hidden'
? 'profile-displayed'
: 'profile-hidden')
}
return(
<div>
<Banner onClickHandler={profileHandler} />
<Profile style={style} />
</div>
)
}
// Render
ReactDOM.createRoot(
document.getElementById("root")
).render(
<App />
);
.profile-hidden {
display: none;
}
.profile-displayed {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
<div id="root"></div>
You cannot use this syntax for React Components COMPONENT.method
, in your case onClick={Profile.changeStyle} !
Instead you should make Banner parent component and use Profile component as child inside it or vise versa !
then You should pass the state style as props so then you will be able to use its value.
your code should look like this :
function Banner() {
const [style, setStyle] = useState("profile-hidden");
const changeStyle = () => {
console.log("you just clicked");
setStyle("profile-displayed");
};
return (
<div className="banner">
<span className="bannerbtnsettings">
<button className="btnbannersettings">Settings</button>
</span>
<span className="bannerbtnprofile">
<button className="btnbannerprofile" onClick={changeStyle}>Profile</button>
</span>
<Profile style={style} />
</div>
);
}
export default Banner;
and your Profile component :
const Profile = (props) => {
return (
<div>
<div className={props.style}> hellllo</div>
</div>
)
}

How to display the list of followers on the profile of all users

I try to display the list of followers and followings on the profile of the users using their id, when I implement there is only the list of followers and followings of the current user that is connected, even if I follow the profile of another user
My Component
import React, { useState } from "react";
import { useSelector } from "react-redux";
import { Link, useParams } from "react-router-dom";
import Modal from "../Components/Log/Modals";
import { isEmpty } from "../Components/Utils";
import FollowHandler from "./FollowHandler";
const FollowingPopup = ({ followerId, type }) => {
const userData = useSelector((state) => state.userReducer);
const [Opening, setOpening] = useState(false);
const { id: userId } = useParams();
const [followingsPopup, setFollowingsPopup] = useState(false);
const usersData = useSelector((state) => state.usersReducer);
return (
<Modal open={!Opening} onClick={() => setOpening(Opening)}>
<span
onClick={() => setOpening(!Opening)}
className="followers__modal__close"
></span>
<header className="followers__modal__header">
<button
className="followers active-followers"
>
Mes Abonnements
</button>
</header>
{/**Les personnes suivis par l'utilsateur */}
{!followingsPopup && (
<>
<div className="followers__modal__content">
<ul>
{userId ? (usersData.map((user) => {
let follower = user.follower.map((users) => users.followerId)
for (let i = 0; i < usersData.length; i++) {
if (user.id !== userId && follower.includes(user.id)) {
return (
<li key={user.id} className="followers__modal__followers">
<a
href={`/${user.id}`}
className="followers__modal__followers__links"
>
<img src={user.picture} alt="" />
<h4>{user.firstname + " " + user.lastname} </h4>
</a>
{user.id !== userData.id ? < FollowHandler followerId={user.id} type={'suggestion'} /> : null}
</li>
);
}
}
return null;
})) : null}
</ul>
</div>
</>
)}{" "}
{/**Les personnes abonnés à l'utilsateur */}
</Modal>
);
};
export default FollowingPopup;
this code works perfectly and I manage to display the list of followers of current user who is logged in but when I click on the profile of another user, the same list of followers of the user who is logged in is displayed too, not the selected profile
[
Your for loop is unnecessary there.
This might be working for you but without a sandbox to test it's hard to tell.
const userFollowers =usersData.find(user=>user.id===userId).followers.map(follower=>follower.id);
usersData.map(user=>{
if(userFollowers.includes(user.id)){
return (
<li key={user.id} className="followers__modal__followers">
<a
href={`/${user.id}`}
className="followers__modal__followers__links"
>
<img src={user.picture} alt="" />
<h4>{user.firstname + " " + user.lastname} </h4>
</a>
{user.id !== userData.id ? < FollowHandler followerId={user.id} type={'suggestion'} /> : null}
</li>
)
})

How to fetch unique item from redux with conditional operator?

I am trying to fetch only unique data from the redux store but got all values from the redux store.
I use usePerma hook to check id from URL and id of redux data then apply ternary operator but did not get desired output. See the output image and code and please tell how to get only one value from that.
When URL is 1002 only 1002 is item should show but its also showing 1001 item.
Here is my code:
import React from 'react'
import { NavLink } from 'react-router-dom';
import { useStateValue } from './Stateprovider';
import { useParams } from 'react-router';
const User = () => {
const [{basket}, dispatch] = useStateValue();
const {id} = useParams();
return (
<>
{basket.map((item) => (
<>
{
({id} === {item.id}) ? //Error
<div key="id" className="card">
<img src={item.Image} alt="" />
<div className="card-data">
<h3><span>User Id: </span>{item.id}</h3>
<h2><span>Name: </span>{item.name}</h2>
</div>
<NavLink className="button" exact to='/home' > User Info </NavLink>
</div>
: null
}
</>
))}
</>
)
}
export default User;
If I understand your question correctly, you want to map only the items from the basket array that match the id match param. For this you can either use Array.prototype.find to first find the matching item and conditionally render if found, or use Array.prototype.filter and just filter the array inline.
Filtering the array inline:
const User = () => {
const [{basket}, dispatch] = useStateValue();
const { id } = useParams();
return (
<>
{basket
.filter(item => item.id === id)
.map((item) => (
<div key={item.id} className="card">
<img src={item.Image} alt="" />
<div className="card-data">
<h3><span>User Id: </span>{item.id}</h3>
<h2><span>Name: </span>{item.name}</h2>
</div>
<NavLink className="button" exact to='/home'>
User Info
</NavLink>
</div>
))
}
</>
)
}
Finding first, then rendering: remember that .find returns undefined if no match is found
const User = () => {
const [{basket}, dispatch] = useStateValue();
const { id } = useParams();
const item = basket.find(item => item.id === id);
return item ? (
<div className="card">
<img src={item.Image} alt="" />
<div className="card-data">
<h3><span>User Id: </span>{item.id}</h3>
<h2><span>Name: </span>{item.name}</h2>
</div>
<NavLink className="button" exact to='/home'>
User Info
</NavLink>
</div>
) : null;
}

React array mapping, toggles all drop-downs on click, I want to open the dropdown for the clicked card only

TextQuoteCard
import React, {useRef, useState} from 'react'
import {Link} from "react-router-dom";
import {QuoteCardDropdown} from "../../utils/dropdowns";
export const TextQuoteCard = () => {
const [open, setOpen] = useState(false)
const toggle = () => setOpen(!open)
const [textQuote, setTextQuote] = useState([
{
userId: '123',
userName: 'Tr',
userImageUrl: 'https://qph.fs.quoracdn.net/main-thumb-892821828-200-lrcgeycqieflgsovvoxglqawinbcjhtv.jpeg',
quoteId: 'TQ122',
postDateTime: 'Fri',
quoteAuthorId: '123',
quoteAuthorName: 'Jhon Mart',
quoteCategory: 'Motivational',
quoteType: 'textQuote',
quoteText: 'If there’s no market, about finding market opportunities, or creating opportunities. If there’s no market, then you need to grow one',
quoteImageUrl: 'https://qph.',
bookmarkStatus: 2,
likesCount: 3300,
commentsCount: 123,
overallShareCount: 1203,
fbShareCount: 423,
twtShareCount: 1232,
waShareCount: 1023,
viewCount: 1923
},
{
userId: '124',
userName: 'nr',
userImageUrl: 'https://qph.fi.jpeg',
quoteId: 'TQ123',
postDateTime: 'Fri',
quoteAuthorId: '123',
quoteAuthorName: 'Wall Mart',
quoteCategory: 'Motivational',
quoteType: 'textQuote',
quoteText: 'Best thing to do. ',
quoteImageUrl: '',
bookmarkStatus: 1,
likesCount: 3300,
commentsCount: 123,
overallShareCount: 1203,
fbShareCount: 423,
twtShareCount: 1232,
waShareCount: 1023,
viewCount: 1923
}
])
const handleBookmark = (event) => {
console.log(event)
}
const idGetter = (id) =>{
console.log(id)
}
const test = Object.keys(textQuote).map(item => item)
console.log(test)
return(
<div>
{
textQuote.map((quote) => (
<div className="QuoteCardPrimaryContainer" key={quote.quoteId}>
<div>{quote.userName}</div>
<div className="ddContainer">
<span className="QuoteCardEngagementActionButtonIconContainer">
<span className="QuoteCardEngagementActionButtonIcon"
onClick={() => toggle(!open)}
>
options
</span>
</span>
{open && <QuoteCardDropdown targetLink={quote.quoteId}/>}
</div>
</div>
))
}
</div>
)
}
**
QuoteCardDropdown.js
import React, {useState} from 'react'
import {Link} from "react-router-dom";
import '../../global/assets/css/dropdowns.css'
export const QuoteCardDropdown = (props) => {
const [ddItems, SetDdItems] = useState([
{
ddOptionIcon: 'icon',
ddOptionText: 'Share',
ddOptionTip: 'Tip text goes here',
ddOptionBorder: 'no',
targetId: props.targetId,
targetLink: props.targetLink
},
{
ddOptionIcon: 'icon',
ddOptionText: 'Bookmark',
ddOptionTip: 'Tip text goes here',
ddOptionBorder: 'no',
targetId: props.targetId,
targetLink: props.targetLink
}
])
return (
<div>
<div className="quoteCardDropdownPrimaryContainer">
<div className="quoteCardDropdownPrimaryBody">
<div className="quoteCardDropdownPrimaryBodyInner">
{
ddItems.map(item => (
<Link to=
{
item.ddOptionText === 'Edit this Quote' ?
`${'edit/' + props.targetLink}` :
item.ddOptionText === 'Share' ?
`${'share/' + props.targetLink}` : ''
}
>
<div className="quoteCardDropdownContentWrapper">
<div className="quoteCardDropdownContentItem">
<div className="quoteCardDropdownItem" key={item.ddOptionText}>
{item.ddOptionText}
</div>
</div>
</div>
</Link>
))
}
</div>
</div>
</div>
<div className="quoteCardPointer" data-placement='top'> </div>
</div>
)
}
I have array of objects mapping to which showed multiple card on-page/feed. each card has a dropdown that the user can perform several actions for the clicked card. think of FB feed or any other social media feed card that the user can click to open a dropdown and pick option for the card. I am trying to achieve something similar but the problem is when I click on the button to open the dropdown it toggles all the dropdowns for all the cards instead of opening the dropdown for the clicked card.
Expected Behavior: open the dropdown for the clicked card only.
Change the open to take the id:
const [open, setOpen] = useState() // undefined is nothing open
const toggle = id => setOpen(open === id ? undefined : id) // close if currently open
// the JSX
return(
<div>
{textQuote.map((quote) => (
<div className="QuoteCardPrimaryContainer" key={quote.quoteId}>
<div>{quote.userName}</div>
<div className="ddContainer">
<span className="QuoteCardEngagementActionButtonIconContainer">
<span className="QuoteCardEngagementActionButtonIcon"
onClick={() => toggle(quote.quoteId)}>
options
</span>
</span>
{open === quote.quoteId && <QuoteCardDropdown targetLink={quote.quoteId}/>}
</div>
</div>
))}
</div>
)
Yes, you are trying to control all dropdowns using a single state variable open.
{open && <QuoteCardDropdown targetLink={quote.quoteId}/>}
When you click on any dropdown it will toggles open and then all dropdowns will open because that single variable controls all of them.
Instead, what you can do is maintain a separate state variable for each dropdown.
I have an example to maintain separate state variable for dropdown-
toggle = (index) => {
this.setState(prevState => {
[`open+${index}`]: !prevState[`open${index}`]
}
}
This way you can keep track of/or toggles open for particular dropdown you just need to change below code -
{
textQuote.map((quote, index) => ( //add 2nd parameter as index
<div className="QuoteCardPrimaryContainer" key={quote.quoteId}>
<div>{quote.userName}</div>
<div className="ddContainer">
<span className="QuoteCardEngagementActionButtonIconContainer">
<span className="QuoteCardEngagementActionButtonIcon"
onClick={() => this.toggle(index)}
>
options
</span>
</span>
{ this.state[`open${index}`] && <QuoteCardDropdown targetLink={quote.quoteId}/>}
</div>
</div>
</div>
))
}
Note - Unfortunately I am not aware of handling state as dynamically inside the function component, but I have given you the exact same use case using class component.
Use an object indexed by quote ID to track a card's open status by the quote ID. I copied only the relevant code, make sure this has all of the other code you need:
export const TextQuoteCard = () => {
const [openById, setOpenById] = useState({});
const toggle = (quoteId) => {
setOpenById((currOpenById) => {
const nextOpenById = { ...currOpenById };
if (currOpenById[quoteId]) {
delete nextOpenById;
} else {
nextOpenById[quoteId] = true;
}
return nextOpenById
})
}
// ...removed code not relevant to example
return (
<div>
{textQuote.map((quote) => (
<div className="QuoteCardPrimaryContainer" key={quote.quoteId}>
<div>{quote.userName}</div>
<div className="ddContainer">
<span className="QuoteCardEngagementActionButtonIconContainer">
<span
className="QuoteCardEngagementActionButtonIcon"
onClick={() => toggle(quote.quoteId)}
>
options
</span>
</span>
{openById[quote.quoteId] && <QuoteCardDropdown targetLink={quote.quoteId} />}
</div>
</div>
))}
</div>
);
};

Reset pagination to the first page by clicking a button outside the component

I'm using material UI usePagination hook to create a custom pagination component, so far so good, the functionality works as expected but I was wondering how I can be able to reset the pagination to the first page by triggering a button that is not part of the pagination component.
Does anyone has an idea on how to trigger that?
This is my component.
import React from "react";
import PropTypes from "prop-types";
import { usePagination } from "hooks";
function arrow(type) {
return (
<i
className={`fa fa-chevron-${
type === "next" ? "right" : "left"
} page-icon`}
/>
);
}
function Pagination({ data, itemCount, onChange }) {
const { items } = usePagination({
count: Math.ceil(data.length / itemCount, 10),
onChange
});
return (
<nav aria-label="Paginator">
<ul className="pagination-component">
{items.map(({ page, type, selected, ...item }, index) => {
let children;
if (type === "start-ellipsis" || type === "end-ellipsis") {
children = "…";
} else if (type === "page") {
children = (
<button
type="button"
automation-tag={`page-${page}`}
className={`page-button ${selected ? "selected" : ""}`}
{...item}
>
{page}
</button>
);
} else {
children = (
<button
automation-tag={type}
className="page-button"
type="button"
{...item}
>
<span className="d-none">{type}</span>
{arrow(type)}
</button>
);
}
return (
// eslint-disable-next-line react/no-array-index-key
<li key={index} className="page-item">
{children}
</li>
);
})}
</ul>
</nav>
);
}
What I'm trying is to create a select component that the onChange function will sort the data, depending on the selection, but when the data is sorted I want to return the pagination component to the first page
const TableVizContainer = props => {
const [currentPage, setCurrentPage] = useState(1);
const [sortColumn, setSortColumn] = useState(1);
const [range, setRange] = useState({
start: 0,
end: 25
});
const onChangePage = (_event, page) => {
setCurrentPage(page);
setRange({
start: 25 * (page - 1),
end: 25 * page
});
};
const onSelectChange = event => {
const { value } = event.target;
setCurrentPage(1);
setSortColumn(parseInt(value, 10));
};
return (
<div
className="table-viz-container container-fluid my-4 float-left"
automation-tag={`table-viz-${automationId}`}
>
<div className="d-flex justify-content-between mb-3 leaderboard-meta">
<span className="leaderboard-title">{visualization.title}</span>
<div className="mr-5">
<label htmlFor="sort-table-select">
Sort By:
<select
id="sort-table-select"
onChange={onSelectChange}
value={sortColumn}
>
{visualization.columns.map((column, index) => {
const uniqueId = uuidv1();
return (
<option key={uniqueId} value={index}>
{setSelectValue(column, visualization.metrics)}
</option>
);
})}
</select>
</label>
</div>
</div>
<div className="d-block d-sm-flex justify-content-between align-items-center my-2 px-2">
<span className="page-items-count" automation-tag="pagination-count">
{`Showing ${range.start === 0 ? 1 : range.start + 1} - ${
range.end <= visualization.rows.length
? range.end
: visualization.rows.length
} of ${visualization.rows.length}.`}
</span>
<Pagination
currentPage={currentPage}
data={visualization.rows}
itemCount={25}
onChange={onChangePage}
/>
</div>
</div>
);
};
Does anyone has an idea on how to reset and move the pagination page to the first one without clicking the component?
There are two ways.
1. Passing Props
Let's just say you have a function called jump() and passing 1 as an argument will reset the pagination. So, you can pass the jump function as a property and reuse that on other components.
function jump(){
setCurrentPage(1)
}
<MyCompnent resetPage={jump} />
// MyComponent
function MyComponent({resetPage}){
return (
<button onClick={resetPage(1)}></button>
)
}
2. On Changing Route
You can reset your pagination when your route will change. For example, you are using a router npm package and that package has a method called onChange or routeChangeStart. With those methods or after creating that method you can implement a function like below.
Router.events.on("routeChangeStart", () => {
jump(1);
});

Resources