Set each active className on react Tab components - reactjs

I'm trying to add active class on each tabtitle so that it can have some styles when clicked. but I have no idea how to add active class on this components, please tell me some solutions, I really appreciate all the help.
App.tsx
import React from "react"
import Tabs from "../Tabs"
import Tab from "../Tabs/Tab"
function App() {
return (
<Tabs>
<Tab title="Lemon">Lemon is yellow</Tab>
<Tab title="Strawberry">Strawberry is red</Tab>
<Tab title="Pear">Pear is green</Tab>
</Tabs>
)
}
Tabs.tsx
import React, { ReactElement, useState } from "react"
import TabTitle from "./TabTitle"
type Props = {
children: ReactElement[]
}
const Tabs: React.FC<Props> = ({ children }) => {
const [selectedTab, setSelectedTab] = useState(0)
return (
<div>
<ul>
{children.map((item, index) => (
<TabTitle
key={index}
title={item.props.title}
index={index}
setSelectedTab={setSelectedTab}
/>
))}
</ul>
{children[selectedTab]}
</div>
)
}
export default Tabs
Tabtitle.tsx
import React, { useCallback } from "react"
type Props = {
title: string
index: number
setSelectedTab: (index: number) => void
}
const TabTitle: React.FC<Props> = ({ title, setSelectedTab, index }) => {
const onClick = useCallback(() => {
setSelectedTab(index)
}, [setSelectedTab, index])
return (
<li>
<button onClick={onClick}>{title}</button>
</li>
)
}
export default TabTitle
Tab.tsx
import React from 'react'
type Props = {
title: string
}
const Tab: React.FC<Props> = ({ children }) => {
return <div>{children}</div>
}
export default Tab
Here is the source.
https://medium.com/weekly-webtips/create-basic-tabs-component-react-typescript-231a2327f7b6

So personally what I would do is:
<TabTitle
key={index}
title={item.props.title}
click={() => setSelectedTab(index)}
selected = (selectedTab === index)
/>
and
const TabTitle: React.FC<Props> = ({ key, title, click, selected}) => {
return (
<li className={selected ? "shiny" : "not-shiny"} key={key}>
<button onClick={click}>{title}</button>
</li>
)
}
That way we don't need to worry about passing the index from tabTitle ALL the way back up, we can simply hand it a function to trigger with the variables pre-filled (we already have access to them within the map method!).
As for actually passing the info about whether or not the tab is selected, we can simply check in the map method whether the current index is = the one held in state. If this evaluates to true we give the tabtitle one class, and if not we give it another.
Note there are lots of different ways to achieve this, this is just the first one that came to mind.
If you wanted to share state information between many different components lower in the tree you might want to check out using React Context providers and the useContext hook.
Also I'm not sure item.props works like that?? I might be wrong though? Maybe someone else will confirm.
Hope this is helpful.

import React from "react"
import Tabs from "../Tabs"
import Tab from "../Tabs/Tab"
function App() {
return ( <Tabs>
<Tab title="Lemon">Lemon is yellow</Tab>
<Tab title="Strawberry">Strawberry is red</Tab>
<Tab title="Pear">Pear is green</Tab>
</Tabs> )
}

Related

Is there a way of supplying a void function and supplying the innards of function when invoked in another place?

Basically what I'm trying to achieve is that, let's say that I have a function in a parent component in React. In a child component i want to do some calculation like lets say distance to nearest elements containing box. But I want to invoke this method via a button click or something in the parent. I have refs pointing to my child component and I can achieve this if I add the click to these children. But since I don't have acces to the parent method; how am I to achieve such behaviour?
Here's my child component:
import { FC, useRef } from 'react'
import styles from './Card.module.scss'
const Card: FC<CardProps> = ({ image, title, info }) => {
const cardRef = useRef<HTMLDivElement>(null)
const scroll = (offset: number) => {
if (cardRef.current) {
cardRef.current.scrollLeft += offset
}
}
return (
<div className={styles.card} ref={cardRef}>
<img src={image} alt={image.split('.')[0]} height={170} width={96} />
<div className={styles['card-info-container']}>
<h3>{title}</h3>
<p>{info}</p>
<img src="Chevron.svg" alt="Chevron" />
</div>
</div>
)
}
export default Card
interface CardProps {
image: string
title: string
info: string
}
and here is my parent component:
import Card from '../Card/Card'
import styles from './Campaigns.module.scss'
import { campaignData } from '../../mockdata/campaigndata'
const Campaigns = () => {
const scroll = (offset: number) => {}
return (
<section className={styles.campaigns}>
<h3></h3>
<div className={styles['cards-container']}>
{campaignData.map(({ image, title, description }, index) => (
<Card image={image} title={title} info={description} key={index} />
))}
</div>
</section>
)
}
export default Campaigns
I think I've found a solution but it's not really an elegant one in my opinion. Instead of agonizing over creating refs directly in the child I've opted to create an array of refs from the length of the content using React's createRef fucntion here's what I have in my parent component.
import Card from '../Card/Card'
import styles from './Campaigns.module.scss'
import { campaignData } from '../../mockdata/campaigndata'
import { createRef } from 'react'
const Campaigns = () => {
const refs = campaignData.map(() => createRef())
console.log(refs)
return (
<section className={styles.campaigns}>
<h3></h3>
<div className={styles['cards-container']}>
{campaignData.map(({ image, title, description }, index) => (
<Card
image={image}
title={title}
info={description}
key={index}
forwardRef={refs[index]}
/>
))}
</div>
</section>
)
}
export default Campaigns
Now I can reference every element as a seperate entity and without the hassle

Using props with loops to pass multiple data from parent to child component

App.jsx
import React from "react";
import Navbar from "./Navbar";
function App() {
return (
<div>
<Navbar
navLink1="about-us"
navLink2="projects"
navLink3="services"
navLink4="blog"
/>
</div>
);
}
export default App;
Navbar.jsx
import React from "react";
export default function Navbar(props) {
const navHeader = "Logo";
const navItems = ["About Us", "Projects", "Services", "Blog"];
const navLinks = navItems.map(addNavLink);
function addNavLink(value, index) {
return "props.navLink" + String(index + 1);
}
return (
<nav>
<h3 className="nav-header">{navHeader}</h3>
{navItems.map((itemValue, itemIndex) =>
navLinks.map(
(linkValue, linkIndex) =>
itemIndex === linkIndex && (
<a
href={linkValue.replaceAll('"', '')}
className="nav-links"
key={itemIndex}
>
<div className="nav-items">{itemValue}</div>
</a>
)
)
)}
</nav>
);
}
I want to use props along with loops so that its name can be changed slightly to match each custom attributes of Navbar component. For instance:
props.navLink1
props.navLink2
props.navLink3
props.navLink4
I tried to use props by first storing them as a string and then removed the double inverted commas in order to use the functionality of props. However, this didn't work. I'm relatively new to React and I'm not sure if it's even possible but if you have any idea to accomplish this then help me out.
This is the output I'm looking for by using loops and props.

Re-Rendering a component

I'm doing a simple todo list using React. What I fail to do is to remove an item once I click on the button.
However, if I click delete and then add a new item, it's working, but only if I add a new todo.
Edit:I've edited the post and added the parent componenet of AddMission.
import React,{useState}from 'react';
import { Button } from '../UI/Button/Button';
import Card from '../UI/Card/Card';
import classes from '../toDo/AddMission.module.css'
const AddMission = (props) => {
const [done,setDone]=useState(true);
const doneHandler=(m)=>{
m.isDeleted=true;
}
return (
<Card className={classes.users}>
<ul>
{props.missions.map((mission) => (
<li className={mission.isDeleted?classes.done:''} key={mission.id}>
{mission.mission1}
<div className={classes.btn2}>
<Button onClick={()=>{
doneHandler(mission)
}} className={classes.btn}>Done</Button>
</div>
</li>
)) }
</ul>
</Card>
);
};
export default AddMission;
import './App.css';
import React,{useState} from 'react';
import { Mission } from './components/toDo/Mission';
import AddMission from './components/toDo/AddMission';
function App() {
const [mission,setMission]=useState([]);
const [isEmpty,setIsEmpty]=useState(true);
const addMissionHandler = (miss) =>{
setIsEmpty(false);
setMission((prevMission)=>{
return[
...prevMission,
{mission1:miss,isDeleted:false,id:Math.random().toString()},
];
});
};
return (
<div className="">
<div className="App">
<Mission onAddMission={addMissionHandler}/>
{isEmpty?<h1 className="header-title">Start Your Day!</h1>:(<AddMission isVisible={mission.isDeleted} missions={mission}/>)}
</div>
</div>
);
}
const doneHandler=(m)=>{
m.isDeleted=true;
}
This is what is causing your issue, you are mutating an object directly instead of moving this edit up into the parent. In react we don't directly mutate objects because it causes side-effects such as the issue you are having, a component should only re-render when its props change and in your case you aren't changing missions, you are only changing a single object you passed in to your handler.
Because you haven't included the code which is passing in the missions props, I can't give you a very specific solution, but you need to pass something like an onChange prop into <AddMission /> so that you can pass your edited mission back.
You will also need to change your function to something like this...
const doneHandler = (m) =>{
props.onChange({
...m,
isDeleted: true,
});
}
And in your parent component you'll then need to edit the missions variable so when it is passed back in a proper re-render is called with the changed data.
Like others have mentioned it is because you are not changing any state, react will only re-render once state has been modified.
Perhaps you could do something like the below and create an array that logs all of the ids of the done missions?
I'm suggesting that way as it looks like you are styling the list items to look done, rather than filtering them out before mapping.
import React, { useState } from "react";
import { Button } from "../UI/Button/Button";
import Card from "../UI/Card/Card";
import classes from "../toDo/AddMission.module.css";
const AddMission = (props) => {
const [doneMissions, setDoneMissions] = useState([]);
return (
<Card className={classes.users}>
<ul>
{props.missions.map((mission) => (
<li
className={
doneMissions.includes(mission.id)
? classes.done
: ""
}
key={mission.id}
>
{mission.mission1}
<div className={classes.btn2}>
<Button
onClick={() => {
setDoneMissions((prevState) => {
return [...prevState, mission.id];
});
}}
className={classes.btn}
>
Done
</Button>
</div>
</li>
))}
</ul>
</Card>
);
};
export default AddMission;
Hope that helps a bit!
m.isDeleted = true;
m is mutated, so React has no way of knowing that the state has changed.
Pass a function as a prop from the parent component that allows you to update the missions state.
<Button
onClick={() => {
props.deleteMission(mission.id);
}}
className={classes.btn}
>
Done
</Button>;
In the parent component:
const deleteMission = (missionId) => {
setMissions(prevMissions => prevMissions.map(mission => mission.id === missionId ? {...mission, isDeleted: true} : mission))
}
<AddMission missions={mission} deleteMission={deleteMission} />

How to filter and sort product in ReactJs by Dropdown component of React bootstrap

I am creating a filter function for the product list in UI for the user. But I have an issue that, I do not know and never ever try with this function before, so I really difficult to resolve it. I have only 1 day left to do that, so I was very confused
This is my Dropdown Component
import React from "react";
import { Dropdown as BootstrapDropdown } from "react-bootstrap";
import PropTypes from "prop-types";
import "../Dropdown/index.css";
const Dropdown = ({ items }) => {
return (
<BootstrapDropdown className="sort-dropdown">
<BootstrapDropdown.Toggle
className="sort-dropdown-toggle"
variant="success"
id="dropdown"
>
<span className="toggle-text">Selection</span>
</BootstrapDropdown.Toggle>
<BootstrapDropdown.Menu className="sort-dropdown-menu">
{items.map((name, index) => (
<BootstrapDropdown.Item
className="sort-dropdown-item"
key={index}
href={`#/action-${index}`}
>
{name}
</BootstrapDropdown.Item>
))}
</BootstrapDropdown.Menu>
</BootstrapDropdown>
);
};
Dropdown.propTypes = {
items: PropTypes.array,
};
Dropdown.defaultProps = {
items: [],
};
export default Dropdown;
And this is my page, which the place I get the Dropdown component
import React from "react";
import { Row } from "react-bootstrap";
import Group from "../../../components/Group/index";
import Dropdown from "../../../components/Dropdown/index";
import "../GroupBar/index.css";
const GroupBar = () => {
return (
<Row className="group-bar">
<Group
title="Product group"
element={<Dropdown items={["Milk Tea", "Juice"]} />}
/>
<Group
title="Sort by price"
element={<Dropdown items={["Low to hight", "Hight to low"]} />}
/>
</Row>
);
}
export default GroupBar;
I would like to filter (by category) and sort (by price) my product page by items of the dropdown. When I select that item, the product will be filtered according to the item I chose.
This is my product list page
import React, { useEffect } from "react";
import { Container, Row, Col } from "react-bootstrap";
import ProductItem from "../../../components/ProductItem/index";
import Loading from "../../../components/Loading";
import PropTypes from "prop-types";
import "../../../common/index.css";
import "../ProductList/index.css";
const ProductList = ({ products, loading, fetchProductRequest }) => {
useEffect(() => {
fetchProductRequest();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (loading) {
return (
<Container>
<Row>
<Col>
<Loading />
</Col>
</Row>
</Container>
);
}
return (
<Container>
<Row>
{!!products && products.length > 0 ? (
products.map((product, index) => {
return (
<ProductItem
key={index}
image={product.image}
name={product.name}
price={product.price}
/>
);
})
) : (
<h4 className="center-title">Product list is empty!</h4>
)}
</Row>
</Container>
);
};
export default ProductList;
This is the page for that,
The list of product and the filter/sort are located in the same folder but different files. Like this
The Group bar it is contain the filter/sort. I get all values by redux, saga
The main page, contain all of them is here
import React from "react";
import { Container } from "react-bootstrap";
import GroupBar from "./GroupBar";
import ProductContainer from "../../containers/ProductContainer";
import Carousel from "../../components/Carousels";
import "../Product/index.css";
const Product = () => {
return (
<Container fluid className="p-0">
<Carousel />
<Container>
<GroupBar />
<ProductContainer />
</Container>
</Container>
);
};
export default Product;
How can I filter related to my list product when it different file like that.
Please anyone help me with this my problem, I just have one day to finish that function, I already research on the internet but it's doesn't make me understand more because it so different from my code and I can not apply that code for mine.
I really really need your support and help as well as you can, the full the better. It's not just helped me to understand also for others like me who are doesn't try it before also see the code is easy to understand too.
I always welcome all of your comments. That is my pleasure. Thank you so much.
You will need your components to keep track of some state. You can read about how to do that here and here.
Once you understand the concept of state, you need to keep track of which item in the list is selected. So for example, you need to keep a state variable that tracks whether "Milk Tea" is selected or "Juice" is selected.
Then, once you have that state, you can display your items using filter or sort on the items list.
Personally, I recommend using class components instead of function components, but here is a minimal working example using function components:
import React, { useState } from 'react';
import { Dropdown as BootstrapDropdown } from 'react-bootstrap';
import './App.css';
const Dropdown = (props) => {
return (
<BootstrapDropdown>
<BootstrapDropdown.Toggle variant='success' id='dropdown'>
<span>Selection</span>
</BootstrapDropdown.Toggle>
<BootstrapDropdown.Menu>
{props.items.map((name, index) => (
<BootstrapDropdown.Item
key={index}
onClick={(event) => {
console.log(event.target.text);
props.setSelected(event.target.text);
}}
value={name}
>
{name}
</BootstrapDropdown.Item>
))}
</BootstrapDropdown.Menu>
</BootstrapDropdown>
);
};
function App() {
const [typeFilter, setTypeFilter] = useState('');
const allItems = [
{ name: 'Coffee Milk Tea', type: 'Tea' },
{ name: 'Earl Gray Milk Tea', type: 'Tea' },
{ name: 'Orange Juice', type: 'Juice' },
{ name: 'Wheatgrass Juice', type: 'Juice' },
];
const itemsToShow = allItems
.filter((item) => {
if (typeFilter) {
return item.type === typeFilter;
}
return true;
})
.map((item, i) => {
return <li key={i}>{item.name}</li>;
});
return (
<div>
<Dropdown items={['Tea', 'Juice']} setSelected={setTypeFilter} />
<ol>{itemsToShow}</ol>
</div>
);
}
export default App;
Notice that the App component stores the state, and passes its state setter to the Dropdown component. The Dropdown gets the setter in its props and uses it to set the App's state when an option is clicked. The App then uses its state to determine which items to show (using items.filter).
This is an example of Lifting state up. Normally, we would think of tracking which item is selected as the job of the dropdown. But, since we need to access that state in another component, we have to "lift up" that state to something higher in the tree. In this small example case, it was App that stored the state. In general, if the tree looks like this:
A
B
C
D
E
F
G
H
and you want to share state between G and D, you need to put that state inside of A because A is the closest parent of both G and D. If you want to share state between C and D, then you need to put that state inside B, because B is the parent of C and D.
In reference to the comment below, you probably want to keep the state for which thing in the dropdown is selected inside of your Product component. Then you need to pass the state setter down the props chain all the way into the Dropdown component, which can call that setter and update the state.
Sorry to hear about your tight schedule. Hopefully this answer can be of some use to you.

Is there a way in React Javascript to pass props and use it in external import?

I want to pass props from one component to another, and use it in the second one for an import above the component declaration
This is for using the same component, with no need to create it 4 times, every time with another SVG.
I'm using React, Javascript, Webpack, babel.
I'm also using svgr/webpack to create a component from an SVG picture, and it's crucial for me to use SVG not < img >.
import React from 'react';
import RightNavItem from './right_nav_item';
const RightNav = ({navitems}) => {
const rightNavItems = navitems.map( (item) => {
return <RightNavItem name={ item }/>
});
return(
<div className="rightnav">
{rightNavItems}
</div>
);
};
.
export default RightNav;
import React from 'react';
const RightNavItem = ({ name }) => {
const svgpath = `../../../../resources/img/navbar/${name}.svg`;
return(
<div>
<img src={ svgpath } style={{height: '25px'}}/>
<span>{ name }</span>
</div>
);
};
export default RightNavItem;
And I want to achieve being able to do this:
import React from 'react';
import SvgPicture from '../../../../resources/img/navbar/{name}.svg';
const RightNavItem = ({ name }) => {
return(
<div>
<SvgPicture />
<span>{ name }</span>
</div>
);
};
export default RightNavItem;
.
Ok so I went back and implemented the whole thing on my local app to get exactly what you need. I am editing my original answer. Hope this solves your issue.
The parent:
import React from 'react';
import { ReactComponent as svg } from 'assets/img/free_sample.svg';
import RightNavItem from './RightNavItem';
const LOGOS = [
{ name: 'home', svg },
{ name: 'home', svg },
];
const RightNav = () => (
<div>
{LOGOS.map(logo => (
<RightNavItem name={logo.name}>
<logo.svg />
</RightNavItem>
))}
</div>
);
export default RightNav;
The child:
import React from 'react';
const RightNavItem = ({ name, children }) => (
<div>
{children}
<span>{name}</span>
</div>
);
export default RightNavItem;
You don't need to import the svg as I did, if you are able to use svg as a component in your webpack config then continue to do what you were doing before.
I managed to do it in a kind of ugly way, but it works.
The problem is if I have more than 4 items, then using it without the map() function can be really annoying.
I used {props.children}, and instead of using map(), I added the 4 times, each with different 'SVG' component child and different props 'name', that way the component only gets initialized at the RightNavItem level.
IF SOMEONE KNOWS how can I use this with the map() function, It'll help a lot!
Thanks to everyone who helped!
For example:
const RightNav = (props) => {
return(
<div className = "rightnav">
<RightNavItem name = {home}>
<HomeSVG />
</RightNavItem>
<RightNavItem name = {profile}>
<ProfileSVG />
</RightNavItem>
.
.
.
</div>
);
};
And in the RightNavItem:
const RightNavItem = (props) => {
return(
<div>
{props.children}
<span>{ props.name }</span>
</div>
);
};

Resources