How to load active class for link in react - javascript

Created one simple tab for loading components, tabs are working fine but a "active" class loading for all Navlinks, on rendering. "active" class should load only for active tab. please help to fix this
import React, { useState } from 'react';
import { NavLink, Outlet } from "react-router-dom";
function SidenavSection(props) {
const [active, setActive] = useState("tab1")
return (
<div>
<div >
<div sm="3" className='border sideNav'>
<NavLink to="" className="btn btn-link" onClick={() => setActive("tab1")}>Overview</NavLink> <br/>
<NavLink to="" className="btn btn-link" onClick={() => setActive("tab2")}>Align & Ratio</NavLink> <br/>
<NavLink to="" className="btn btn-link" onClick={() => setActive("tab3")}>Avatar</NavLink> <br/>
</div>
<div sm="9" className='border'>
<div>
{active === "tab1" && <div>section component 1</div>}
{active === "tab2" && <div>section component 2</div>}
{active === "tab3" && <div>section component 3</div>}
</div>
</div>
</div>
</div>
);
}
export default SidenavSection;

First of all, you don't need to use NavLink for this.
You can achieve active class by adding a ternary operator for the classnames.
import React, { useState } from "react";
import { NavLink } from "react-router-dom";
export default function App() {
const [active, setActive] = useState("tab1");
return (
<div>
<div>
<div sm="3" className="border sideNav">
<p
to=""
className={`btn btn-link ${active === "tab1"? "active":""}`}
onClick={() => setActive("tab1")}
>
Overview
</p>{" "}
<br />
<p
to=""
className={`btn btn-link ${active === "tab2" && "active"}`}
onClick={() => setActive("tab2")}
>
Align & Ratio
</p>{" "}
<br />
<p
to=""
className={`btn btn-link ${active === "tab3" && "active"}`}
onClick={() => setActive("tab3")}
>
Avatar
</p>{" "}
<br />
</div>
<div sm="9" className="border">
<div>
{active === "tab1" && <div>section component 1</div>}
{active === "tab2" && <div>section component 2</div>}
{active === "tab3" && <div>section component 3</div>}
</div>
</div>
</div>
</div>
);
}
You should however try making an object or array including the links data, so you can easily loop through without much code, and setting active link also will be easier this way.
import React, { useState } from "react";
import { NavLink } from "react-router-dom";
export default function App() {
const [active, setActive] = useState("0");
const els = [
{tab:1, name: "Overview",},
{tab:2, name: "Avatar"},
{tab:3, name: "Align & Ratio"},
]
return (
<div>
<div>
<div sm="3" className="border sideNav">
{
els.map((item, index)=>{
return <p
onClick={()=>{setActive(index)}}
key={index}
className={index === active ? "active" : ""}
>
{item.name}
</p>
})
}
<br />
</div>
<div sm="9" className="border">
<div>
{active === "tab1" && <div>section component 1</div>}
{active === "tab2" && <div>section component 2</div>}
{active === "tab3" && <div>section component 3</div>}
</div>
</div>
</div>
</div>
);
}

You need to either use routing/conditional displaying using Javascript, here your trying to use both.
Without routing
import React, { useState } from 'react';
import { NavLink, Outlet } from "react-router-dom";
function SidenavSection(props) {
const [active, setActive] = useState("tab1")
const isActive = (key) => (active === key ? 'active' : '');
return (
<div>
<div >
<div sm="3" className='border sideNav'>
<a href="" className=`btn btn-link ${isActive('tab1')}` onClick={() => setActive("tab1")}>Overview</a> <br/>
<a href="" className=`btn btn-link ${isActive('tab2')}` onClick={() => setActive("tab2")}>Align & Ratio</a> <br/>
<a href="" className=`btn btn-link ${isActive('tab3')}` onClick={() => setActive("tab3")}>Avatar</a> <br/>
</div>
<div sm="9" className='border'>
<div>
{active === "tab1" && <div>section component 1</div>}
{active === "tab2" && <div>section component 2</div>}
{active === "tab3" && <div>section component 3</div>}
</div>
</div>
</div>
</div>
);
}
export default SidenavSection;

Related

How do I connect Posts to Upvotes - ReactJS

I have a laravelapi that fetches posts from the database and another one that fetches upvotes.
Now I want to display posts on my react component and show the length of upvotes for every particular post.
Here is how I have tried it out. But im getting the value as undefined on the console. Please help. Here is my code
import React, { useState, useEffect } from 'react';
import { useNavigate } from "react-router-dom";
import Menubar from "../../components/menubar/Menubar"
import PostSkeleton from '../../components/skeleton/PostSkeleton';
import PostWidget from '../../components/widgets/PostWidget';
import axios from 'axios';
function Community() {
let navigate = useNavigate();
const [posts, setPosts] = useState([]);
const [upvotes, setUpvotes] = useState([]);
const [limit, setLimit] = useState(4);
const [isLoading, setIsLoading] = useState(true);
const [loading, setLoading] = useState(false);
function handleClick() {
navigate("/add-post");
}
useEffect(() => {
let isMounted = true;
axios.get(`/api/posts?page=1&limit=` + limit).then(res => {
if (isMounted) {
if (res.data.status === 200) {
setPosts(res.data.posts);
setIsLoading(false);
setLoading(false);
}
}
});
return () => {
isMounted = false
};
}, [limit]);
const loadMore = () => {
setLimit(limit + 4)
setLoading(true);
}
useEffect(() => {
let isMounted = true;
axios.get(`/api/upvotes`).then(res => {
if (isMounted) {
if (res.data.status === 200) {
setUpvotes(res.data.upvotes);
}
}
});
return () => {
isMounted = false
};
}, []);
return (
<div>
<Menubar />
<div className="appHeader bg-primary text-light">
<div className="left">
<a onClick={() => navigate(-1)} className="headerButton goBack">
<i className="fi fi-rr-angle-left"></i> </a>
</div>
<div className="pageTitle">Community</div>
<div className="right">
<a href="#" className="headerButton">
<i className="fi fi-rr-paper-plane"></i> </a>
</div>
</div>
<div id="appCapsule">
<div className="section">
<div className="post-input mt-3">
<form>
<a href="profile.php" className="btn btn-icon btn-secondary rounded mr-1" >
<img src="assets/img/sample/avatar/avatar4.jpg" alt="avatar" className="avatar imaged rounded" />
</a>
<div className="form-group boxed">
<div className="input-wrapper">
<input type="text" onClick={handleClick} className="form-control" placeholder="Tell the World Something" />
</div>
</div>
</form>
</div>
</div>
<div className="section mt-2 mb-3">
{isLoading && <PostSkeleton cards={4} />}
{posts.map((post) => (<PostWidget post={post} upvotes={upvotes.find(upvotes =>posts.postid === upvotes.post_id)} key={post.postid} />))}
<div className="text-center">
<a onClick={loadMore} className={limit <= posts.length ? 'btn btn-text-primary mr-1' : 'btn btn-text-primary mr-1 disabled'} >{loading ? <><span className="spinner-border spinner-border-sm mr-05" role="status" aria-hidden="true"></span>Loading More</> : <>{limit <= posts.length ? <>Load More <i className="fi fi-rr-angle-small-right"></i></> : 'All Posts Loaded'} </>}</a>
</div>
</div>
</div>
</div>
);
}
export default Community;
The PostWidget Component
import React, { useState, useEffect } from 'react';
import { Link } from "react-router-dom";
import { LazyLoadImage } from "react-lazy-load-image-component";
import axios from 'axios';
import toast, { Toaster } from 'react-hot-toast';
const PostWidget = ( {post, upvotes}) => {
console.warn(upvotes && upvotes.length)
const formatDate = (dateString) => {
const options = { year: "numeric", month: "long", day: "numeric" }
return new Date(dateString).toLocaleDateString(undefined, options)
}
return (
<div>
<Toaster />
<div className="comment-block mb-3 pb-1">
<div className="comment-header">
<div className="avatar">
<img src="assets/img/sample/avatar/avatar1.jpg" alt="avatar" className="imaged w32 rounded mr-1" />
</div>
<div className="comment-other">
<h4 className="title">{post.user && post.user.firstname} {post.user && post.user.lastname}</h4>
<span className="time">{formatDate(post.created_at)}</span>
</div>
</div>
<div className="item">
<div className="in">
<div className="post-image mt-1">
<Link to={"/post-details/" + post.postid}>
<LazyLoadImage src={`http://localhost:8000/${post.postimage}`} alt="avatar" className="imaged w-100" />
</Link>
<Link to={"/fruit-details/" + post.fruit.id}>
<div className="chip mt-1 mr-1">
<span className="chip-label">{post.fruit.name}</span>
</div>
</Link>
</div>
<Link to={"/post-details/" + post.postid}>
<div className="text mt-1">
{post.postcontent}
</div>
</Link>
<div className="comment-footer">
<a href="" className="comment-button">
<i className="fi fi-rr-check"></i>
Upvote (5)
</a>
<a href="" className="comment-button">
<i className="fi fi-rr-arrow-down"></i>
Downvote
</a>
<Link to={"/post-details/" + post.postid} className="comment-button" >
<i className="fi fi-rr-comment"></i>
Comment
</Link>
</div>
</div>
</div>
</div>
</div>
);
}
export default PostWidget;
upvotes={upvotes.find(upvotes =>posts.postid === upvotes.post_id)}
I think you need to use post.postId instead of posts.postid
This seems likely to be incorrect:
upvotes.find(upvotes =>posts.postid === upvotes.post_id)
Probably you meant to use the post being iterated over in the predicate, not the posts collection.
upvotes.find(upvote =>post.postid === upvote.post_id)

change animation/css class on button click

import React, { useState } from 'react'
import SliderUp from '#svg/icons/slider-up.svg'
import SliderDown from '#svg/icons/slider-down.svg'
export const Slide=()=> {
// show and hide state on individual button click
const [toggleState, setToggleState] = useState(1);
const toggleTab = (index:number) => {
setToggleState(index);
};
//hover between buttons state through the sliderup and sliderdown button
const changeState = (direction:string) => setToggleState(prev =>
{
const curr = direction === 'down' ? prev + 1 : prev - 1;
return curr < 1 ? 2 : curr > 2 ? 1 : curr
});
return (
<div>
{/* buttons to the left */}
<div>
<button onClick={() => changeState('up')} >
<SliderUp />
</button>
<ul >
<li>
<button className={toggleState===1 ? "inline-block w-3 h-3 " : "inline-block w-1 h-1"} onClick={() => toggleTab(1)} >`
</button>
</li>
<li>
<button className={toggleState===2 ? "inline-block w-3 h-3 " : "inline-block w-1 h-1"}
onClick={() => toggleTab(2)}>
</button>
</li>
</ul>
I want to change components class "animate-slideB" to "animate-slideT" on buttonclick with the state down
<button onClick={() => changeState('down')} >
<SliderDown />
</button>
</div>
{/* components to the right */}
<div>
<div className="w-full ">
{toggleState == 1 &&
<div className='animate-slideB'>
<TopPart/> // component
</div>}
{toggleState == 2 &&
<div className='animate-slideB'>
<MythicCol/> //component
</div>}
</div>
</div>
)
}
I tried this method of adding a function to the button with change state "down" and giving id="sampleDiv" to div elements containing components.But it didnt work.
animate-slideB: animation from top to bottom,
animate-slideT: animation from bottom to top
const handlebuttonclass=()=>{
let sampleElem= document.getElementById("sampleDiv");
if(sampleElem?.className==='animate-slideB'){
sampleElem.className= 'animate-slideT'
}
else{
sampleElem!.className= 'animate-slideB';
}
}

Can't get Qty to show in Increment and Decrement Component

I'm pretty new to react, and I am trying to make an Increment and decrement component in in my product screen. The issue that I'm having is that I would like it to only be able to increment if the there are that many items in stock, but when I try to do this the component stops showing the number associated with amount of the item chosen. I would really appreciate any help or guidance on how to do this.
Thank you!
ProductScreen.js
import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import { detailsProduct } from '../actions/productActions';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
import { IconContext } from 'react-icons';
import { FiPlus, FiMinus } from 'react-icons/fi';
export default function ProductScreen(props) {
const dispatch = useDispatch();
const productId = props.match.params.id;
const [qty, setQty] = useState(1);
const productDetails = useSelector((state) => state.productDetails);
const { loading, error, product } = productDetails;
const handleQuantity = (type) => {
if (type === "dec") {
qty > 1 && setQty(qty - 1);
}else if(product.countInStock >= product.qty){
setQty(qty + 1);
}
};
return (
<div>
{loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<div>
<Link to="/body">Back to result</Link>
<div className="row top">
<div className="col-1">
<div className="card card-body">
<ul>
<li>
<div className="row">
<div>Status</div>
<div>
{product.countInStock > 0 ? (
<span className="success">In Stock</span>
) : (
<span className="danger">Unavailable</span>
)}
</div>
</div>
</li>
{product.countInStock > 0 && (
<>
<li>
<div className="row">
<div>Qty</div>
<div className="AddContainer">
<div className="AmountContainer">
<FiMinus onClick={() => handleQuantity("dec")}/>
<div className="Amount">{product.qty}</div>
<FiPlus onClick={() => handleQuantity("inc")}/>
</div>
</div>
<div>
</div>
</div>
</li>
</>
)}
</ul>
</div>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
);
}

How to open only specific modal window?

I need to open only that modal div to which I made a click. But they all open when I click on any element.
How to make it to only that modal, to which I make a click?
App.js
import { useEffect, useState } from "react";
import Modal from "./components/Modal";
import "./style.css";
function App() {
useEffect(() => {
fetch(
"https://api.unsplash.com/photos/?client_id=cf49c08b444ff4cb9e4d126b7e9f7513ba1ee58de7906e4360afc1a33d1bf4c0"
)
.then((res) => res.json())
.then((result) => setItem(result));
}, []);
const [item, setItem] = useState([]);
const [modalActive, setModalActive] = useState(false);
return (
<div className="app">
{item.map((item) => (
<div
className="image-container"
key={item.id}
onClick={() => setModalActive(true)}
>
<img className="image" src={item.urls.regular} alt="logo" />
<div className="info">
<img
className="avatar"
src={item.user.profile_image.small}
alt="avatar"
/>
<div className="text">
<p className="name">{item.user.username}</p>
<p className="name">{item.alt_description}</p>
</div>
</div>
<Modal
active={modalActive}
setActive={setModalActive}
url={item.urls.regular}
/>
</div>
))}
</div>
);
}
export default App;
Modal.jsx
import React from "react";
import "./index.css";
const Modal = ({ setActive, active, url }) => {
return (
<div
className={active ? "modal active" : "modal"}
onClick={() => setActive(false)}
>
<div className="modal__img">
<img src={url}/>
</div>
</div>
);
};
export default Modal;
Other solution is to use the index to show or hide the modal:
<div className="app">
{item.map((item, index) => (
<div
className="image-container"
key={item.id}
onClick={() => setModalActive(index)}
>
<img className="image" src={item.urls.regular} alt="logo" />
<div className="info">
<img
className="avatar"
src={item.user.profile_image.small}
alt="avatar"
/>
<div className="text">
<p className="name">{item.user.username}</p>
<p className="name">{item.alt_description}</p>
</div>
</div>
<Modal
active={modalActive === index}
setActive={setModalActive}
url={item.urls.regular}
/>
</div>
))}
</div>

Table Body in Semantic UI React with No Table Rows Causing validateDomNesting Error

I've gone through all of the example questions on this and can't seem to figure out what my problem is.
Here is the full error:
index.js:1375 Warning: validateDOMNesting(...): Text nodes cannot appear as a child of <tbody>.
in tbody (created by TableBody)
in TableBody (at Favorites.js:167)
in table (created by Table)
in Table (at Favorites.js:123)
in Favorites (created by ConnectFunction)
in ConnectFunction (at App.js:73)
in Route (at App.js:69)
in Router (created by BrowserRouter)
in BrowserRouter (at App.js:46)
in div (created by Container)
in Container (at App.js:44)
in App (created by ConnectFunction)
in ConnectFunction (at src/index.js:10)
in Provider (at src/index.js:9)
and my full code
import React, { useState, useEffect } from "react";
import Search from "./Search";
import { connect } from "react-redux";
import {
Table,
Popup,
Responsive,
Button,
Segment,
Header,
Image
} from "semantic-ui-react";
import { searchChange } from "../reducers/searchReducer";
import { fetchData } from "../reducers/baseballReducer";
import { removeFavorite } from "../reducers/favoriteReducer";
import { getFavorites } from "../reducers/favoriteReducer";
import { getUpdates } from "../reducers/updateReducer";
import { setNotification } from "../reducers/notificationReducer";
import _ from "lodash";
var moment = require("moment");
moment().format();
//Filter 'favupdates' state for user's input
const searchCards = ({ favUpdates, search }) => {
return search
? favUpdates.filter(a =>
a.title
.toString()
.toLowerCase()
.includes(search.toLowerCase())
)
: favUpdates;
};
const style = {
borderRadius: 0,
padding: "2em"
};
const Favorites = props => {
useEffect(() => {
document.title = "My Favorites | All Vintage Search";
}, []);
useEffect(() => {
props.getFavorites(props.loggedUser.id);
}, [props.loggedUser]);
//Set 'filteredData' state
useEffect(() => {
setData(props.cardsToShow);
}, [props]);
const mapFAVS = props.favorites;
const data = Array.from(mapFAVS);
const updatedFavs = data.map(item => item.id);
const formatFavs = updatedFavs.map(id => id.join(","));
console.log("FORMAT FAVS", formatFavs);
//Get updated data from eBay based on user's favorite id's and update 'favUpdates' state
useEffect(() => {
props.getUpdates(formatFavs);
}, [props.favorites]);
const [column, setColumn] = useState(null);
const [direction, setDirection] = useState(null);
const [filteredData, setData] = useState(props.cardsToShow);
console.log("Filtered Data", filteredData);
const handleSortNumeric = clickedColumn => {
const sorter = data => parseInt(data[clickedColumn]);
setData(_.sortBy(filteredData, sorter));
};
const handleSortReverse = () => {
const sorter = data => parseInt(data);
setData(_.sortBy(filteredData, sorter).reverse());
};
const handleSort = clickedColumn => {
if (column !== clickedColumn) {
setColumn(clickedColumn);
if (clickedColumn === "title" || "acceptsOffers" || "timeStamp") {
setData(_.sortBy(filteredData, [clickedColumn]));
} else {
handleSortNumeric(clickedColumn);
}
setDirection("ascending");
return;
}
if (clickedColumn === "title") {
setData(_.sortBy(filteredData.reverse()));
} else {
handleSortReverse();
}
direction === "ascending"
? setDirection("descending")
: setDirection("ascending");
};
const removeFavorite = card => {
props.removeFavorite(card, props.loggedUser);
props.setNotification(`You removed ${card.title}!`, 5);
};
if (!props.cardsToShow) return null;
return (
<>
<Search />
<Segment inverted color="blue">
<Header inverted color="grey" size="medium">
My Favorites
</Header>
</Segment>
<Segment>Count: {props.cardsToShow.length}</Segment>
<Responsive maxWidth={767}>
<strong>Click to Sort:</strong>
</Responsive>
<Table sortable celled fixed striped>
<Table.Header>
<Table.Row>
<Table.HeaderCell
textAlign="center"
sorted={column === "title" ? direction : null}
onClick={() => handleSort("title")}
>
Card Title
</Table.HeaderCell>
<Table.HeaderCell
width={2}
textAlign="center"
sorted={column === "updatedBids" ? direction : null}
onClick={() => handleSort("updatedBids")}
>
# Bids
</Table.HeaderCell>
<Table.HeaderCell
textAlign="center"
sorted={column === "updatedPrice" ? direction : null}
onClick={() => handleSort("updatedPrice")}
>
Price
</Table.HeaderCell>
<Table.HeaderCell
textAlign="center"
sorted={column === "timeStamp" ? direction : null}
onClick={() => handleSort("timeStamp")}
>
Time Left
</Table.HeaderCell>
<Table.HeaderCell
textAlign="center"
sorted={column === "status" ? direction : null}
onClick={() => handleSort("status")}
>
Status
</Table.HeaderCell>
<Table.HeaderCell textAlign="center" width={2}>
Remove
</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{!filteredData
? "Sorry No Cards Found"
: filteredData.map(card => (
<>
<Responsive maxWidth={767}>
<div className="ui piled compact segment">
<div className="ui card">
<div className="blurring dimmable image">
<div className="ui inverted dimmer">
<div className="content">
<div className="center">
<div className="ui red button view">
VIEW
</div>
</div>
</div>
</div>
<Image
src={card.image}
href={card.itemURL}
centered
style={{ padding: "5px" }}
/>
</div>
<div className="content">
<div
id="rate"
className="ui star rating right floated"
data-rating="3"
></div>
<div className="header">
<a href={card.itemURL}>{card.title}</a>
</div>
<div
className="meta"
style={{ padding: "5px 0 0 0" }}
>
<span className="date">
<i className="clock icon"></i> Ends in{" "}
{moment
.duration(card.timeLeft, "minutes")
.humanize()}
</span>
<div style={{ padding: "10px 0 0 0" }}>
<span>
<Button color="green">
${card.updatedPrice}
</Button>
</span>
<span class="right floated date">
{" "}
<Button
onClick={() => removeFavorite(card)}
color="red"
icon="remove circle"
/>
</span>
</div>
</div>
</div>
<div className="extra content">
<div
className="ui right labeled button"
data-content="Bids"
data-variation="tiny"
tabindex="0"
>
<div className="ui blue icon tiny button">
<i className="gavel large icon"></i>
</div>
<a
href={card.itemURL}
className="ui basic blue left pointing label"
>
{card.updatedBids}
</a>
</div>
<div
className="ui left labeled right floated button"
data-content="Watch Count"
data-variation="tiny"
tabindex="0"
>
<a
href={card.itemURL}
className="ui basic blue right pointing label"
>
{card.status}
</a>
<div className="ui blue icon tiny button">
<i className="history large icon"></i>
</div>
</div>
</div>
</div>
</div>
</Responsive>
<Responsive
as={"tr"}
minWidth={768}
style={{ width: "100%" }}
>
<Popup
trigger={
<Table.Cell>
<a href={card.itemURL} target={"_blank"}>
{card.title}
</a>
</Table.Cell>
}
content={
<img
alt={card.title}
src={card.image}
height="250"
></img>
}
style={style}
size="small"
position="left center"
></Popup>
<Table.Cell textAlign="center">
{card.updatedBids}
</Table.Cell>
<Table.Cell textAlign="center">
${card.updatedPrice}
</Table.Cell>
<Table.Cell textAlign="center">
{moment.duration(card.timeLeft, "minutes").humanize()}
</Table.Cell>
<Table.Cell textAlign="center">{card.status}</Table.Cell>
<Table.Cell textAlign="center">
<Button
onClick={() => removeFavorite(card)}
color="red"
icon="remove circle"
/>
</Table.Cell>
</Responsive>
</>
))}
</Table.Body>
<Table.Footer>
<Table.Row>
<Table.HeaderCell colSpan="6"></Table.HeaderCell>
</Table.Row>
</Table.Footer>
</Table>
</>
);
};
const mapStateToProps = state => {
return {
baseball: state.baseball,
favorites: state.favorites,
favUpdates: state.favUpdates,
loggedUser: state.loggedUser,
page: state.page,
entries: state.entries,
query: state.query,
pageOutput: state.pageOutput,
search: state.search,
cardsToShow: searchCards(state)
};
};
const mapDispatchToProps = {
searchChange,
fetchData,
removeFavorite,
getFavorites,
getUpdates,
setNotification
};
export default connect(mapStateToProps, mapDispatchToProps)(Favorites);
I suspected the issue might be where i'm using the section as I dont have any table rows or cells set within the Table Body. I tried wrapping that whole section with a Table.Row and a Table.Cell, but still getting the same error. Any ideas?
If !filteredData === false then the only child of <Table.Body> is text.
As the error says, the table body cannot have text as a child.
Wrap the text like this <Table.Row><Table.Cell>Sorry no cards shown</Table.Cell></Table.Row>

Categories