How to send unique props to similar Parents? - reactjs

I currently made a custom component that acts similar to a carousel called Scrollbar. When I select a specific item it dynamically loads the information and displays a content div that has information on the item such as id, name, thumbnail-Image, etc..
The problem I am trying to solve is that I want to display multiple Scrollbars and only have one content div active at a time. The issue is that each Scrollbar has their own content div. I need a way to communicate which one is active.
I'm thinking possibly I need another component lets call it Home that displays multiple Scrollbars and onClick, it sets the current one Active and the rest inActive. All the Scrollbars should still be displaying however, one dynamic content div will appear below the active Scrollbar.
This sounds like I need to send one Scrollbar's prop isActive = true and the others isActive = false. I am unsure how to achieve this.
Here are short versions of my components:
Scrollbar.js
const Scrollbar = ({category, isActive }) => {
return (
<div className="scrollbar">
<h2>{category}</h2>
<div className="item-wrapper" style={{maxWidth: `${scrollbarWidth}px`}}>
.
.
.
.
//Handles Navigating the Scrollbar Left and Right
<SlideButton type="prev" onClick={handlePrev}/>
<SlideButton type="next" onClick={handleNext}/>
</div>
//Dynamic Content gets shown here when user clicks on an item & Scrollbar (tbd)
{isActive && currentCard && <Content activeCard={currentCard} clickFunc={handleCardChange} /> }
</div>
);
}
export default Scrollbar;
Home.js
const Home = () => {
return (
<div className="home">
<Scrollbar key="0" category="Scroll 1" activeBar={null} />,
<Scrollbar key="1" category="Scroll 2" activeBar={null} />
</div>
);
}
export default Home;

The Home component should hold the id of who is the activeBar with useState. It should pass setActiveBar to each Scrollbar.
const Home = () => {
const [activeBar, setActiveBar] = useState(0)
return (
<div className="home">
<Scrollbar
key="0"
id="0"
category="Scroll 1"
activeBar={activeBar}
setActiveBar={setActiveBar}
/>,
<Scrollbar
key="1"
id="1"
category="Scroll 2"
activeBar={activeBar}
setActiveBar={setActiveBar}
/>
</div>
);
}
The Scrollbar component should check if it's id is identical to activeBar, and set isActive accordingly. It should also set itself to be the activeBar when clicked.
const Scrollbar = ({category, id, activeBar, onClick }) => {
const isActive = activeBar === id;
return (
<div
onClick={() => setActiveBar(id)}
className="scrollbar"
>

Related

Entire list re-renders due to the click handler

I am using Material UI to render a grid of cards with a button on it. The problem I have is that every time I change a Select component on the parent, it re-renders the entire grid and it is really slow.
He is a simplified version of the set up.
Whenever I change the Select component, the entire grid re-renders.
// Page.js
export default function Page() {
const [target, setTarget] = useState('
');
const handleButtonClick = (cardName) => {
console.log(`${target} was the target of ${cardName}`}
}
return(
<div
<TargetSelect setTarget={setTarget} />
<Grid>
<GridCard cardName="Card 1" handleButtonClick={handleButtonClick} />
<GridCard cardName="Card 2" handleButtonClick={handleButtonClick} />
<GridCard cardName="Card 3" handleButtonClick={handleButtonClick} />
<GridCard cardName="Card 4" handleButtonClick={handleButtonClick} />
</Grid>
</div
)
}
What am I doing wrong that this is happening?

How to display a specific content of a specific button if this button(out of 2 or more buttons) is clicked in Material UI and React?

How can I display the specific content that is assigned to a specific button in which if that specific button is clicked, it will display the assigned content of that specific button.
In my case, I have three buttons named "BBQ", "Bilao", and Chicken". These specific buttons contain their own specific content(which are also buttons but in a form of menu items containing their info).
Issue: My problem is that I cannot seem to display those specific content even if one of those specific buttons are clicked. For example, if "BBQ" will be clicked, it should display its specific menu item contents, the same process goes with "Bilao" and "Chicken".
I utilized a useState and an array of categories which contain the specific button names("BBQ", "Bilao", "Chicken) and also mapping the array. But all of these doesn't seem to work.
Problem now(pic):
Source code:
import * as React from "react";
import { useState } from "react";
import { Stack } from "#mui/material";
import ButtonCategoryStyle from "./ButtonCategoryStyle";
import BBQButtons from "./categoryButtons/BBQButtons";
import BilaoButtons from "./categoryButtons/BilaoButtons";
import ChickenButtons from "./categoryButtons/ChickenButtons";
export default function HomeOrderPage() {
const categories = ["BBQ", "Bilao", "Chicken"];
const [myCategory, setMyCategory] = useState("");
return (
<div>
<Stack spacing={0} direction="row">
{categories.map((category) => (
<ButtonCategoryStyle
title={category.toLocaleUpperCase()}
key={category}
onClick={() => setMyCategory(category)}
/>
))}
</Stack>
<div>
<p>
{myCategory === "police" && <BBQButtons />}
{myCategory === "chef" && <BilaoButtons />}
{myCategory === "doctor" && <ChickenButtons />}
</p>
</div>
</div>
);
}
Codesandbox link: https://codesandbox.io/s/vibrant-germain-76p1er?file=/src/HomeOrderPage.jsx:0-1025
How it should look like:
BBQ:
Bilao:
Chicken:
I based my code structure into this YT video, demo, and its Github source code:
https://www.youtube.com/watch?v=jRxoO-Zd0pQ (YT)
https://react-component-depot.netlify.app/react-basics/show-hide-elements (Demo)
https://github.com/codegeous/react-component-depot/blob/master/src/pages/ReactBasics/ShowAndHide/index.js (Github)
There are similar Stack questions that involves this process but still most of them did not work well for me.
Hoping for your guides and responses since I am exploring MUI and React at the moment and it will be a big help for me. Thank you very much!
There are two problems in your code.
{/*
HomeOrderPage.jsx
You have forgotten to change category names. Yours are "police, chef, doctor" right now.
*/}
{myCategory === "BBQ" && <BBQButtons />}
{myCategory === "Bilao" && <BilaoButtons />}
{myCategory === "Chicken" && <ChickenButtons />}
// styles removed just not to take space
// You need to use rest operator to pass all props
export default function ButtonCategoryStyle({ title, ...restProps }) {
return (
<Button
{...restProps}
>
{title}
</Button>
);
}
// Or you can just pass onClick method.
export default function ButtonCategoryStyle({ title, onClick }) {
return (
<Button
onClick={onClick}
>
{title}
</Button>
);
}

How to NOT render/ hide a React component when no prop is passed?

TLDR: Cannot figure out why component is still being rendered while no props are passed.
So I have been building a NextJS application, and I have this banner component that is shown on every page of my website. It has some header text, buttons and an image:
const Banner = (props) => {
return (
<div className={bannerStyles.wrapper}>
<div className={classnames(bannerStyles.banner, "wrap", "center")}>
<div className={bannerStyles.banner_left}>
<h1>{props.header}</h1>
<div className={bannerStyles.button_wrapper}>
<div className={bannerStyles.button}>
<Button>{props.button || null}</Button>
</div>
<div className={bannerStyles.button}>
<Button>{props.scnd_button || null}</Button>
</div>
</div>
</div>
<div className={bannerStyles.banner_right}>
<Image src={props.image} alt=""></Image>
</div>
</div>
</div>
);
};
Inside of this, as you can see I have two Button components (The MDEast thing is an arrow icon):
const Button = ({children}) => {
return (
<div className={buttonStyles.button}>
<Link href="/"><a>{children} <MdEast /></a></Link>
</div>
)
}
Now I want the option that if no prop is passed, that the Button component(s) do(es) not render/ is hidden from the page, so that it is optional per page. Yet the Button does still render, even though I am not passing any props on my About page. My about page:
const About = () => {
return (
<>
<Banner
header="Hello this is my code"
image={banner_placeholder}
/>
</>
)
}
PS. I am fairly new to React and NextJS, so this might be a beginner mistake, or I am not understanding the fundamentals well enough, but could someone point me in the right direction please?
To conditionally render the button you can use:
props.button && <Button>{props.button}</Button>
When props.button is falsy, then button will not get rendered.

React Button Click Hiding and Showing Components

I have a toggle button that show and hides text. When the button is clicked I want it to hide another component and if clicked again it shows it.
I have created a repl here:
https://repl.it/repls/DapperExtrasmallOpposites
I want to keep the original show / hide text but I also want to hide an additional component when the button is clicked.
How to I pass that state or how do I create an if statement / ternary operator to test if it is in show or hide state.
All makes sense in the repl above!
To accomplish this you should take the state a bit higher. It would be possible to propagate the state changes from the toggle component to the parent and then use it in any way, but this would not be the preferred way to go.
If you put the state in the parent component you can use pass it via props to the needed components.
import React from "react";
export default function App() {
// Keep the state at this level and pass it down as needed.
const [isVisible, setIsVisible] = React.useState(false);
const toggleVisibility = () => setIsVisible(!isVisible);
return (
<div className="App">
<Toggle isVisible={isVisible} toggleVisibility={toggleVisibility} />
{isVisible && <NewComponent />}
</div>
);
}
class Toggle extends React.Component {
render() {
return (
<div>
<button onClick={this.props.toggleVisibility}>
{this.props.isVisible ? "Hide details" : "Show details"}
</button>
{this.props.isVisible && (
<div>
<p>
When the button is click I do want this component or text to be
shown - so my question is how do I hide the component
</p>
</div>
)}
</div>
);
}
}
class NewComponent extends React.Component {
render() {
return (
<div>
<p>When the button below (which is in another component) is clicked, I want this component to be hidden - but how do I pass the state to say - this is clicked so hide</p>
</div>
)
}
}
I just looked at your REPL.
You need to have the visibility state in your App component, and then pass down a function to update it to the Toggle component.
Then it would be easy to conditionally render the NewComponent component, like this:
render() {
return (
<div className="App">
{this.state.visibility && <NewComponent />}
<Toggle setVisibility={this.setVisibility.bind(this)} />
</div>
);
}
where the setVisibility function is a function that updates the visibility state.

React - Setting state to target with onClick method

I am trying to recreate a tabs component in React that someone gave me and I am getting stuck while getting the onClick method to identify the target.
These are the snippets of my code that I believe are relevant to the problem.
If I hardcode setState within the method, it sets it appropriately, so the onClick method is running, I am just unsure of how to set the tab I am clicking to be the thing I set the state to.
On my App page:
changeSelected = (event) => {
// event.preventDefault();
this.setState({
selected: event.target.value
})
console.log(event.target.value)
};
<Tabs tabs={this.state.tabs} selectedTab={this.state.selected}
selectTabHandler={() => this.changeSelected}/>
On my Tabs page:
{props.tabs.map(tab => {
return <Tab selectTabHandler={() => props.selectTabHandler()} selectedTab={props.selectedTab} tab={tab} />
})}
On my Tab page:
return (
<div
className={'tab active-tab'}
onClick={props.selectTabHandler(props.tab)}
>
{props.tab}
</div>
When I console.log(props.tab) or console.log(event.target.value) I am receiving "undefined"
There are a few issues causing this to happen. The first issue is that you wouldn't use event.target.value in the Content component because you aren't reacting to DOM click event directly from an onClick handler as you are in Tab, instead you are handling an event from child component. Also keep in mind that event.target.value would only be applicable to input or similar HTML elements that have a value property. An element such as <div> or a <span> would not have a value property.
The next issues are that you aren't passing the tab value from Tabs to Content and then from within Content to it's changeSelected() handler for selectTabHandler events.
In addition the onClick syntax in Tab, onClick={props.selectTabHandler(props.tab)} is not valid, you will not be able to execute the handler coming from props and pass the props.tab value. You could instead try something like onClick={() => props.selectTabHandler(props.tab)}.
Content - need to pass tab value coming from child to changeSelected():
render() {
return (
<div className="content-container">
<Tabs
tabs={this.state.tabs}
selectedTab={this.state.selected}
selectTabHandler={tab => this.changeSelected(tab)}
/>
<Cards cards={this.filterCards()} />
</div>
);
}
Tabs - need to pass tab coming from child to selectTabHandler():
const Tabs = props => {
return (
<div className="tabs">
<div className="topics">
<span className="title">TRENDING TOPICS:</span>
{props.tabs.map(tab => {
return (
<Tab
selectTabHandler={tab => props.selectTabHandler(tab)}
selectedTab={props.selectedTab}
tab={tab}
/>
);
})}
</div>
</div>
);
};
export default Tabs;
Also don't forget the unique key property when rendering an array/list of items:
<Tab
key={tab}
selectTabHandler={tab => props.selectTabHandler(tab)}
selectedTab={props.selectedTab}
tab={tab}
/>
Here is a forked CodeSandbox demonstrating the functionality.

Resources