Trying to build a champion Select Screen using React - reactjs

I am still learning React and i am trying to build a champion(which in this case is name hashiras) selection screen. I am taking data from an array in the react app and i am trying to select one and display a toggle that this champion(hashira) has been chosen. However i am a bit confused on state and have been struggling with trying to display the name of the chosen hashira among the 4 i have in the array. Also my images are not appearing.
What i am trying to obtain is when i click on choose, the top text will display the name of the chosen hashira.
My final goal is to toggle a form to enter the own username after the hashira is chosen which i will try by myself.
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import {render} from 'react-dom';
import {rengokuimg} from './Images/rengokuIMG.png';
import {sanemiimg} from './Images/rengokuIMG.png';
import {shinobuimg} from './Images/rengokuIMG.png';
import {giyuuimg} from './Images/rengokuIMG.png';
let hashiraList=[
{"name":"Rengoku Kyojiro", "description":"Flame Hashira", "age":20, "element":"Flame","imgsource":rengokuimg,"Choice":"Rengoku Chosen"},
{"name":"Giyuu Tomioka", "description":"Water Hashira", "age":21, "element":"Water","imgsource":giyuuimg,"Choice":"Giyuu Chosen"},
{"name":"Sanemi Shinazugawa", "description":"Wind Hashira", "age":22, "element":"Wind","imgsource":sanemiimg,"Choice":"Sanemi Chosen"},
{"name":"Shinobu Kocho", "description":"Insect Hashira", "age":22, "element":"Poison","imgsource":shinobuimg,"Choice":"Shinobu Chosen"}
]
const Hashira =({name, description,element,age,imgsource,Choice}) => {
return(
<section>
<h2>Name: {name}</h2>
<p>Description: {description}</p>
<div><img src={imgsource}/></div>
<p>Element: {element}</p>
{/* <button onClick={()=>console.log({name},console.log({age}))}>Choose </button> */}
<button onClick={()=>console.log({Choice})}>Select the {element} hashira</button>
</section>
)
}
const NotSelected=()=>
<div>
<h1>No Hashira Selected.</h1>
</div>
const Selected=()=>
<div><h2>You have chosen hashira</h2></div>
class HashiraSelect extends React.Component{
state = {
Chosen : false,
formdisplay :false,
selected:false
}
toggleOpenClosed({name}){
this.setState({
Chosen: !this.state.Chosen,
selected: {name}
})
}
render(){
console.log(this.state)
const {hashiras} = this.props
return(
<div>
{this.state.selected ? <Selected/> : <NotSelected/>}
{/* <h1> Have you chosen a Hashira : {this.state.Chosen ? 'Chosen' : 'Not yet'}</h1> */}
{hashiras.map(
(hashira, idx) =>
<Hashira
key={idx}
name={hashira.name}
description={hashira.description}
age={hashira.age}
element={hashira.element}
Choice={hashira.Choice}
imgsource={hashira.imgsource}
Selected={this.state.Selected}
/>
)}
{/* <button onClick ={this.toggleOpenClosed}>Choose</button> */}
</div>
)
}
}
{/* <Hashira name="Sanemi" description="The wind Hashira." element="Wind"/> */}
render(
<HashiraSelect hashiras={hashiraList}/>,
document.getElementById('root')
)
Thank you once more for the help.
Sandbox link

An onChange handler should be created in HashiraSelect. Pass this handler as a prop to Hashira component. When button is click, pass the selected hashira value to the onChange handler and set it in state.
Check out the WORKING DEMO
const Hashira = ({
name,
description,
element,
age,
imgsource,
Choice,
onChange
}) => {
return (
<div>
<section>
<h2>Name: {name}</h2>
<p>Description: {description}</p>
{/* <div><img src={rengokuimg}/></div> */}
<p>Element: {element}</p>
{/* <button onClick={()=>console.log({name},console.log({age}))}>Choose </button> */}
<button onClick={() => onChange(Choice)}>
Select the {element} hashira
</button>
</section>
</div>
);
};
class HashiraSelect extends React.Component {
state = {
Chosen: false,
formdisplay: false,
choice: null
};
toggleOpenClosed() {
this.setState({
Chosen: !this.state.Chosen
});
}
onChange = choice => {
this.setState({ choice, Chosen: true });
};
render() {
console.log(this.state);
const { hashiras } = this.props;
const { choice } = this.state;
return (
<div>
<h1>
Have you chosen a Hashira : {this.state.Chosen ? "Chosen" : "Not yet"}
</h1>
{choice && <h2>{`Hashira Chosen:${choice}`}</h2>}
{hashiras.map((hashira, idx) => (
<Hashira
key={idx}
name={hashira.name}
description={hashira.description}
age={hashira.age}
element={hashira.element}
Choice={hashira.Choice}
onChange={this.onChange}
// imgsource={hashira.imgsource}
Selected={this.state.Selected}
// Choice={this.state.Chosen}
/>
))}
{/* <button onClick ={this.toggleOpenClosed}>Choose</button> */}
</div>
);
}
}
// <Hashira name="Sanemi" description="The wind Hashira." element="Wind"/>
render(
<HashiraSelect hashiras={hashiraList} />,
document.getElementById("root")
);

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>
)
}

React component re-rendering to default data. Why?

Simple app that loops through my array of birthdays which has 5 birthdays in it by default. Once I delete a birthday and click the "add birthday" button my modal pops up. When I click my "close" button my birthdays component re-renders back to 5 birthdays when I am expecting to have 4 because I deleted one upon opening my modal. Why is it doing this?
Below is my code for my birthdays component, and modal component. I have also attached an image for a visual.
App component:
import react, { useState } from "react";
import Birthdays from "./Birthdays";
import BirthayData from "./BirthdayData";
import { AddBirthdayModal, AddBirthdayButton } from "./AddBirthdayModal";
function App() {
//Keeping track of all birthdays
const [allBirthdays, setAllBirthdays] = useState([...BirthayData]);
//keeping track if add birthday modal is displayed
const [isAddBirthdayShown, setIsAddBirthdayShown] = useState(false);
//Delete selected birthday.
const DeleteBirthday = (indx) => {
const updatedBirthdays = [...allBirthdays];
updatedBirthdays.splice(indx, 1);
setAllBirthdays(updatedBirthdays);
};
return (
<>
<AddBirthdayModal
setIsAddBirthdayShown={setIsAddBirthdayShown}
isAddBirthdayShown={isAddBirthdayShown}
/>
<div className="birthdays-container">
<h2>{allBirthdays.length} Birthdays Today</h2>
<div>
{allBirthdays.length >= 1 &&
allBirthdays.map((user, index) => {
return (
<Birthdays
key={user.id}
selfie={user.image}
name={user.Name}
age={user.AgeTurning}
deleteBirthday={DeleteBirthday}
index={index}
/>
);
})}
</div>
</div>
<div>
<button className="btn" onClick={() => setAllBirthdays([])}>
Clear
</button>
<AddBirthdayButton setIsAddBirthdayShown={setIsAddBirthdayShown} />
</div>
</>
);
}
export default App;
Birthdays Component
import React from 'react'
import "./Birthday.css"
export default function Birthdays(props) {
return (
<div className="birthday-flex-container">
<div className="image-container">
<img
className="user-image center"
src={props.selfie}
alt={`Selfie of person`}
/>
</div>
<div className="user-info center">
<h4 className="name">{props.name}</h4>
<span className="age">{props.age} years</span>
</div>
<div className="delete-birthday-container">
<span onClick={() => props.deleteBirthday(props.index)}>X</span>
</div>
</div>
)
}
Modal Component
import React from "react";
import "./AddBirthdayModal.css";
function AddBirthdayModal({ setIsAddBirthdayShown, isAddBirthdayShown }) {
const showHideClassName = isAddBirthdayShown
? "modal-container display-block"
: "modal display-none";
return (
<div className={showHideClassName}>
<div className="modal">
<p className="modal-title">Who's Birthday?</p>
<div className="form-container">
<form className="birthday-form">
<input type="text" placeholder="Name" />
<br />
<input type="text" placeholder="Age" />
<br />
<label for="myfile">Select a Image: </label>
<input type="file" id="myfile" name="myfile" />
<br />
<button onClick={() => setIsAddBirthdayShown(false)}>Close</button>
</form>
</div>
</div>
</div>
);
}
function AddBirthdayButton({ setIsAddBirthdayShown }) {
return (
<div>
<button className="btn" onClick={() => setIsAddBirthdayShown(true)}>
Add Birthday
</button>
</div>
);
}
export { AddBirthdayModal, AddBirthdayButton };
There is a typo in your import:
import react, { useState } from "react";
Should be:
import React, {useState} from "react";
You are not passing the index parameter to the DeleteMyBirthday function when click delete parameter:
deleteBirthday={DeleteBirthday}
Needs to be:
deleteBirthday={() => DeleteBirthday(index)}
Like this:
import react, { useState } from "react";
import Birthdays from "./Birthdays";
import BirthayData from "./BirthdayData";
import { AddBirthdayModal, AddBirthdayButton } from "./AddBirthdayModal";
function App() {
//Keeping track of all birthdays
const [allBirthdays, setAllBirthdays] = useState([...BirthayData]);
//keeping track if add birthday modal is displayed
const [isAddBirthdayShown, setIsAddBirthdayShown] = useState(false);
//Delete selected birthday.
const DeleteBirthday = (indx) => {
const updatedBirthdays = [...allBirthdays];
updatedBirthdays.splice(indx, 1);
setAllBirthdays(updatedBirthdays);
};
return (
<>
<AddBirthdayModal
setIsAddBirthdayShown={setIsAddBirthdayShown}
isAddBirthdayShown={isAddBirthdayShown}
/>
<div className="birthdays-container">
<h2>{allBirthdays.length} Birthdays Today</h2>
<div>
{allBirthdays.length >= 1 &&
allBirthdays.map((user, index) => {
return (
<Birthdays
key={user.id}
selfie={user.image}
name={user.Name}
age={user.AgeTurning}
deleteBirthday={() => DeleteBirthday(index)}
index={index}
/>
);
})}
</div>
</div>
<div>
<button className="btn" onClick={() => setAllBirthdays([])}>
Clear
</button>
<AddBirthdayButton setIsAddBirthdayShown={setIsAddBirthdayShown} />
</div>
</>
);
}
export default App;
I was able to figure it out.
For some reason when I render my modal component like this
{isAddBirthdayShown && (
<AddBirthdayModal
setIsAddBirthdayShown={setIsAddBirthdayShown}
isAddBirthdayShown={isAddBirthdayShown}
/>
)}
Instead of
const showHideClassName = isAddBirthdayShown
? "modal-container display-block"
: "modal display-none";
It works as expected. I'm not sure why. All I do is attached a class to my modal and make it a "display:block" when isAddBirthday is true and "display: none" to hide it when its false.

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 do I preserve previous state of React InstantSearch

import React, { Component, useState } from 'react';
import algoliasearch from 'algoliasearch/lite';
import {
InstantSearch,
Hits,
SearchBox,
Highlight,
connectRefinementList,
} from 'react-instantsearch-dom';
import PropTypes from 'prop-types';
import './App.css';
const searchClient = algoliasearch(
'test',
'test'
);
class App extends Component {
constructor(props) {
super(props);
this.state = {
selectedCountries: []
}
}
render() {
const RefinementList = ({
items,
isFromSearch,
refine,
searchForItems,
createURL,
}) => {
return (
<ul style={{ listStyle: 'none' }}>
{
items &&
items.map(item => (
<li key={item.label}>
<input
type="checkbox"
checked={item.isRefined}
// href={createURL(item.value)}
style={{ fontWeight: item.isRefined ? 'bold' : '' }}
onChange={event => {
// event.preventDefault();
refine(item.value);
}}
/>
{isFromSearch ? (
<Highlight attribute="label" hit={item} />
) : (
item.label
)}{' '}
({item.count})
</li>
))}
</ul>
)
};
const CustomRefinementList = connectRefinementList(RefinementList);
return (
<div className="container">
<InstantSearch searchClient={searchClient} indexName="parterns">
<div className="search-panel">
<div className="search-panel__results">
<SearchBox
className="searchbox"
translations={{
placeholder: '',
}}
searchAsYouType={false}
/>
<Hits hitComponent={Hit} />
<br />
<br />
<button onClick={(e) => {
const that = this;
e.preventDefault();
that.setState({
selectedCountries: Array.from(new Set([...that.state.selectedCountries, 'India']))
})
}
}
>
Select India
</button>
<br />
<button onClick={(e) => {
const that = this;
e.preventDefault();
that.setState({
selectedCountries: Array.from(new Set([...that.state.selectedCountries, 'USA']))
})
}
}
>
Select Usa
</button>
<br />
<h3>Location</h3>
<div className="region">
<CustomRefinementList
operator="or"
limit={10}
defaultRefinement={[]}
attribute="region" />
</div> <br />
<CustomRefinementList
operator="or"
limit={this.state.selectedCountries.length}
defaultRefinement={this.state.selectedCountries}
attribute="country" />
<br />
<br />
</div>
</div>
</InstantSearch>
</div>
);
}
}
function Hit(props) {
return (
<article onClick={() => alert(props.hit.title)}>
<h1>
<Highlight attribute="title" hit={props.hit} />
</h1>
</article>
);
}
Hit.propTypes = {
hit: PropTypes.object.isRequired,
};
export default App;
The problem is all previously selected filters are getting cleared.
For example initially I click on filter ex: North America
So I will get filtered results for North America.
Now I want to have Country filter which will be visible when click on button ex Select USA
When I am clicking on Button for ex: Select USA then I am setting state because I want to render it dynamically but issue is previous state is getting cleared how can I preserve previous state for component.

how to pass props to a component via onClick event?

Here I'm iterating through the list and displaying clickable {expense.createdAt}. When I click on the {expense.createdAt} I want to show its <ExpenseDetails /> on the same page, so I need to pass the props. How do I do that with onClick?
My code below which doesn't display anything.
class ExpenseList extends Component {
render () {
const {expenses} = this.props;
const list = expenses.expenseList.map(expense =>
<Segment clearing key={expense.uid} >
<a href="" onClick={() => {this.passProps(expense)}}>
{expense.createdAt}
</a>
</Segment>
)
const details = function passProps(expense){
return (
<div>
<ExpenseDetails expense={expense}/>
</div>
)
}
return (
<div>
<Grid celled='internally'>
<Grid.Row>
<Grid.Column width={5}>
<div>
<h1>Your Expense List:</h1>
<Segment.Group raised>
{list}
</Segment.Group>
</div>
</Grid.Column>
<Grid.Column width={11}>
<Segment>
{details}
</Segment>
</Grid.Column>
</Grid.Row>
</Grid>
</div>
)
}
}
When I click on{expense.createdAt} the page refreshes. This might be an issue. How can I prevent that?
console.log(this.props.expenses):
Something like this should work. The important concept is that you need to have a parent to both of your subcomponents passing data. In this case, Expenses is the parent. In the parent, you create a function, showItemDetails that you pass down to ExpenseList. That is your clickhandler, which is used to pass back up the index location of the data you want to render. That showItemDetails then uses this.setState to give ItemDetails's the props you want.
import React, {Component} from 'react'
export default class Expenses extends Component {
constructor(props) {
super(props)
this.state = {
expenseItems: []
}
}
showItemDetails(idx) {
const {expenseItems} = this.props.expenses.expenseList[idx]
this.setState({expenseItems})
}
render() {
const createdDates = this.props.expenses.expenseList.map(({createdAt}) => {
return createdAt
})
const expenseListProps = {
showItemDetails: (idx) => this.showItemDetails(idx),
createdDates
}
const itemDetailsProps = {expenseItems: this.state.expenseItems}
return (
<div>
<ExpenseList {...expenseListProps}/>
<ItemDetails {...itemDetailsProps}/>
</div>
)
}
}
class ExpenseList extends Component{
render() {
const expenseList = this.props.createdDates.map((date, idx) => (
<div key={idx}>
<button onClick={() => this.props.showItemDetails(idx)}>
{date}
</button>
</div>
))
return (
<div>
{expenseList}
</div>
)
}
}
class ItemDetails extends Component {
render() {
debugger;
const items = this.props.expenseItems.map(({uid, date, desc, amount}) => (
<div>
{uid}
{date}
{desc}
{amount}
</div>
))
return (
<div>
{items}
</div>
)
}
}

Resources