React - show two elelments on button click - reactjs

I have a stackblitz here
I know I'm not meant to ask this but is there a better way of doing this.
I have two divs that are positioned on tpo of each other and two buttons to show each of the divs
I'm using useState and updating it on button click <button onClick={() => setBlock('One')}>Btn One</button>
and then show the div based on the useState value.
{block === 'One' && <div className="FlexContainerColOne"></div>}
I'm asking cos I've never seen like this before block === 'One' `and not sure if this the best way to do this
import * as React from 'react';
import './style.css';
import { FlexContainer } from './styled';
const App = () => {
const [block, setBlock] = React.useState('One');
return (
<div>
<button onClick={() => setBlock('One')}>Btn One</button>
<button onClick={() => setBlock('Two')}>Btn Two</button>
<FlexContainer>
{block === 'One' && <div className="FlexContainerColOne"></div>}
{block === 'Two' && <div className="FlexContainerColTwo"></div>}
</FlexContainer>
</div>
);
};
export default App;

If you want to switch between two divs, just create a state to toggle it
const App = () => {
const [toggleBlock, setToggleBlock] = React.useState(true);
return (
<div>
<button onClick={() => setToggleBlock(!toggleBlock)}>Toggle</button>
<FlexContainer>
{toggleBlock ? (
<div className="FlexContainerColOne"></div>
) : (
<div className="FlexContainerColTwo"></div>
)}
</FlexContainer>
</div>
);
};

Related

Open a modal in React when a user wants to delete an image

import getPhotoUrl from 'get-photo-url'
import { useLiveQuery } from 'dexie-react-hooks'
import { db } from '../dexie'
const Gallery = () => {
const allPhotos = useLiveQuery(() => db.gallery.toArray(), [])
const addPhoto = async () => {
db.gallery.add({
url: await getPhotoUrl('#addPhotoInput'),
})
}
const removePhoto = (id) => {
db.gallery.delete(id)
}
return (
<>
<input type="file" name="photo" id="addPhotoInput"/>
<label htmlFor="addPhotoInput" onClick={addPhoto}>
<i className="add-photo-button fas fa-plus-square"></i>
</label>
<section className='gallery'>
{!allPhotos && <p>Loading...</p>}
{allPhotos?.map((photo) => (
<div className="item" key={photo.id}>
<img src ={photo.url} className="item-image" alt=""/>
<button className="delete-button" onClick= {() => removePhoto(photo.id)}>Delete</button>
</div>
))}
</section>
</>
)
}
export default Gallery
Please I need help in the above code. I'm building an instagram clone with React. I have a gallery component in which I want to delete specific images. I would like a modal to pop up asking to confirm if a user would like to delete the image. If they say yes, the image would be deleted, while no leaves the image.
The easiest way is to use the default browser window.confirm
<button className="delete-button" onClick= {() => {
const deleteConfirmed = window.confirm('Are you sure?');
if (deleteConfirmed) {
removePhoto(photo.id)
}
}}>Delete</button>
But if you want to customize the popup, you can create your own or use any component-library like material-ui.

Change back to previous state onClick button and texts

How do I change the text and button text back to its original state with an onClick? I know theres a setTimeout but I only want the change to revert back FOR BOTH the button and text when I click on it. Currently I can only use it one way. If i click on it, it changes from Original to new but I will like to change back to Original if i click on it again. Thanks!
Code:
import React, {useState} from 'react'
const Card = () => {
const[text, setText] =useState('original')
const [buttonText, setButtonText] = useState("Original");
return (
<div className='translate uppercase py-5 break-normal text-center mx-4 space-y-2 font-bold'>
<div className="App">
<button className='rounded-full border-black border-solid'
type="submit"
onClick={() => {
setButtonText("New");
setText('New')
}}
>
{buttonText}
</button>
</div>
<h2>{text}</h2>
</div>
)
}
export default Card;
If you Know both values you can use this solution
import React, {useState} from 'react'
const Card = () => {
const [captions, setCaptions] = useState([
{ buttonText: "orginal", text: "orginal" },
{ buttonText: "new", text: "new" }
]);
const[flag, setFlag] =useState(0)
return (
<div className='translate uppercase py-5 break-normal text-center mx-4 space-y-2 font-bold'>
<div className="App">
<button className='rounded-full border-black border-solid'
type="submit"
onClick={() => {
setCaptions(captions);
setFlag(flag === 0 ? 1 : 0);
}}
>
{captions[flag].buttonText}
</button>
</div>
<h2>{captions[flag].text}</h2>
</div>
)
}
export default Card;
If you don't know new value you can add to captions new value
Try this:
const [isOriginal, setIsOriginal] = useState(true)
onClick={() => {
if(isOriginal) {
setButtonText("New");
setText('New')
setIsOriginal(false)
} else if (!isOriginal) {
setButtonText("Original");
setText('original')
setIsOriginal(true)
}
}
By checking the original state, you can conditionally set text values.

React: Setting onClick method appropriately

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();
}
}
/>

How to show/hide one element from data array rendered dynamically with react

Still fairly new on understanding how to use React hooks. I have a list of questions that was dynamically rendered from an array of objects. When I click on a question, I want only that question's answer to show. Right now, when I click on a question, all of the answers show at the same time.
This is the file containing the data:
export const personalFaq = [
{
question: 'Question 1',
answer: 'Answer 1 '
},
{
question: 'Question 2',
answer: 'Answer 2'
},
{
question: 'Question 3',
answer: 'Answer 3'
},
{
question: 'Question 4',
answer: 'Answer 4'
},
{
question: 'Question 5',
answer: 'Question 5'
}
]
This is my component for rendering my list of questions:
import React, { useState } from 'react';
import { Container, Row } from 'react-bootstrap';
import { personalFaq } from '../../questionData/personalFaq';
const Faq = () => {
const [showAnswer, setShowAnswer] = useState(false);
const onClick = () => setShowAnswer(!showAnswer);
const renderQuestion = (question, index) => {
return (
<div key={index}>
<p><span onClick={onClick}>
{!showAnswer ? (<i className="fas fa-angle-down m-1"></i>) : (<i className="fas fa-angle-up"></i>)}
</span>{question.question}</p>
{showAnswer && (<p>{question.answer}</p>) }
</div>
)
}
return(
<Container>
<Container>
<Container>
<h1>FAQ</h1>
<Row>
{personalFaq.map(renderQuestion)}
</Row>
</Container>
</Container>
</Container>
)
}
export default Faq;
Image of what is currently rendered:
Dynamically Rendered List of Questions
Thanks all! I added a number to each question since they are fixed and use that as the id. I was able to solve it with the code below:
const Faq = () => {
// States to control toggle effect on faq
const [showAnswer, setShowAnswer] = useState(false);
const [currentId, setCurrentId] = useState(null);
//Will render one question and show/hide answer when span is clicked
const renderQuestion = (question) => {
const onClick = () => {
setCurrentId(question.id);
setShowAnswer(!showAnswer);
}
return (
<div key={question.id}>
<p>
<span onClick={onClick}>
{showAnswer ? (<i className="fas fa-angle-up"></i>) : (<i className="fas fa-angle-down m-1"></i>)}
</span>{question.question}</p>
{currentId === question.id && showAnswer === true && (<p>{question.answer}</p>) }
</div>
)
}
//Container where all the questions will be rendered dynamically
return(
<Container>
<Container>
<Container>
<h1>FAQ</h1>
<Row>
{personalFaq.map(renderQuestion)}
</Row>
</Container>
</Container>
</Container>
)
}
It's because you set 1 boolean value for tracking the visibility for all of the items, so if it's turned to true all of them will be displayed.
you also don't pass any parameters to the onClick function that can track which one of the items needs to be displayed or hide
Your state is declared and used within the Faq component, thus it affects all renders within that component that uses it.
In this case the simplest solution is to create a separate component for RenderQuestion that manages its own state and thus should only expand within itself.
Something like this the below example should solve your issue.
RenderQuestion is now its own component that manages its own state. It will be rendered for each item in the personalFaq array, and each item will have its own instance of the component with its own state management.
import React, { useState } from 'react';
import { Container, Row } from 'react-bootstrap';
import { personalFaq } from '../../questionData/personalFaq';
const RenderQuestion = (question) => {
const [showAnswer, setShowAnswer] = useState(false);
const onClick = () => setShowAnswer(!showAnswer);
return (
<div>
<p><span onClick={onClick}>
{!showAnswer ? (<i className="fas fa-angle-down m-1"></i>) : (<i className="fas fa-angle-up"></i>)}
</span>{question.question}</p>
{showAnswer && (<p>{question.answer}</p>)}
</div>
)
}
const Faq = () => {
return (
<Container>
<Container>
<Container>
<h1>FAQ</h1>
<Row>
{personalFaq.map((question, index) => <RenderQuestion key={index} question={question} />)}
</Row>
</Container>
</Container>
</Container>
)
}
export default Faq;
If what you want to do is to be able to see an answer at the time, then you should probably add an id to every item and then use that to set an activeQuestionId and only display that (I left you an example). If you want to be able to see answers independently of each. other, then you should create a local state in a separate component for each row.
import React, { useState } from 'react';
import { Container, Row } from 'react-bootstrap';
import { personalFaq } from '../../questionData/personalFaq';
const Faq = () => {
const [activeQuestionId, setActiveQuestionId] = useState(null)
return (
<Container>
<Container>
<Container>
<h1>FAQ</h1>
<Row>
{personalFaq.map(({ id, question, answer }) => (
<div key={id}>
<p>
<span onClick={() => setActiveQuestionId(id)}>
<i className={`fas ${activeQuestionId === id ? 'fa-angle-up' : 'fa-angle-down m-1'}`}></i>
</span>
{question}
</p>
{activeQuestionId === id && <p>{answer}</p>}
</div>
))}
</Row>
</Container>
</Container>
</Container>
)
}
export default Faq

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