ReactJS: how using queryselector variable out a function - reactjs

I'm using this code:
export default function Naviguation() {
const overBtnMenu = () => {
const navBars = document.querySelectorAll('.nav-bars')
for (let i = 0; i < navBars.length; i++) {
navBars[i].style.width = '40px'
}
}
const outBtnMenu = () => {
const navBars = document.querySelectorAll('.nav-bars')
navBars[0].style.width = '20px'
navBars[1].style.width = '30px'
navBars[2].style.width = '20px'
}
return (
<nav>
<img src={img} className='nav-logo' alt="logo" />
<div className='nav-btn-menu' onMouseEnter={overBtnMenu} onMouseLeave={outBtnMenu}>
<span className='nav-barTop nav-bars'></span>
<span className='nav-barMiddle nav-bars'></span>
<span className='nav-barBottom nav-bars'></span>
</div>
</nav>
)
}
the code runs correctly but I want to use the variable "navBars" once time like this
export default function Naviguation() {
const navBars = document.querySelectorAll('.nav-bars')
const overBtnMenu = () => {
for (let i = 0; i < navBars.length; i++) {
navBars[i].style.width = '40px'
}
}
const outBtnMenu = () => {
navBars[0].style.width = '20px'
navBars[1].style.width = '30px'
navBars[2].style.width = '20px'
}
return (
<nav>
<img src={img} className='nav-logo' alt="logo" />
<div className='nav-btn-menu' onMouseEnter={overBtnMenu} onMouseLeave={outBtnMenu}>
<span className='nav-barTop nav-bars'></span>
<span className='nav-barMiddle nav-bars'></span>
<span className='nav-barBottom nav-bars'></span>
</div>
</nav>
)
}
But the code doesn't run because it wrote on the console "navBars undefined..."
How using a variable out the function to using it into my function?

In React you have the concept of useRef whenever you need to get the value of an actual DOM element.
Rewrite your component as below:
import { useEffect, useRef } from 'react';
export default function Navigation() {
const navBarsRef = useRef(null);
const navBars = navBarsRef.current;
useEffect(() => {
navBarsRef.current = document.querySelectorAll('.nav-bars');
}, []);
const overBtnMenu = () => {
for (let i = 0; i < navBars.length; i++) {
navBars[i].style.width = '40px';
}
};
const outBtnMenu = () => {
navBars[0].style.width = '20px';
navBars[1].style.width = '30px';
navBars[2].style.width = '20px';
};
return (
<nav>
<img src={img} className="nav-logo" alt="logo" />
<div className="nav-btn-menu" onMouseEnter={overBtnMenu} onMouseLeave={outBtnMenu}>
<span className="nav-barTop nav-bars"></span>
<span className="nav-barMiddle nav-bars"></span>
<span className="nav-barBottom nav-bars"></span>
</div>
</nav>
);
}

I wrote your code:
import React, { useEffect, useRef } from 'react';
import img from "../../images/logo-sanimex.webp";
import "./Naviguation.css";
export default function Navigation() {
const navBarsRef = useRef(null);
const navBars = navBarsRef.current;
useEffect(() => {
navBarsRef.current = document.querySelectorAll('.nav-bars');
}, []);
const overBtnMenu = () => {
for (let i = 0; i < navBars.length; i++) {
navBars[i].style.width = '40px';
}
};
const outBtnMenu = () => {
navBars[0].style.width = '20px';
navBars[1].style.width = '30px';
navBars[2].style.width = '20px';
};
return (
<nav>
<img src={img} className="nav-logo" alt="logo" />
<div className="nav-btn-menu" onMouseEnter={overBtnMenu} onMouseLeave={outBtnMenu}>
<span className="nav-barTop nav-bars"></span>
<span className="nav-barMiddle nav-bars"></span>
<span className="nav-barBottom nav-bars"></span>
</div>
</nav>
);
}
But it's still not working :(
console.log shows:
Uncaught TypeError: navBars is null
overBtnMenu Naviguation.js:15

I found the solution !! I just remove "null" from const navBarsRef = useRef()
Thanks very much for helping
import "./Naviguation.css";
export default function Navigation() {
const navBarsRef = useRef();
const navBars = navBarsRef.current;
useEffect(() => {
navBarsRef.current = document.querySelectorAll('.nav-bars');
}, []);
const overBtnMenu = () => {
for (let i = 0; i < navBars.length; i++) {
navBars[i].style.width = '40px';
}
};
const outBtnMenu = () => {
navBars[0].style.width = '20px';
navBars[1].style.width = '30px';
navBars[2].style.width = '20px';
};
return (
<nav>
<img src={img} className="nav-logo" alt="logo" />
<div className="nav-btn-menu" onMouseEnter={overBtnMenu} onMouseLeave={outBtnMenu}>
<span className="nav-barTop nav-bars"></span>
<span className="nav-barMiddle nav-bars"></span>
<span className="nav-barBottom nav-bars"></span>
</div>
</nav>
);
}

Related

How can i use a hook in a conditional statement?

how can i use a hook like below in an if statement? I am trying use a button click to indicate a change in category. once the change in category is set, pagination aligns the count in accordance to.
const [category, setCategory] = useState('')
let count = ContentsCount
if (category) {
count = filteredContentCount
}
/// ...
<ul className="dropdown-menu">
{categories.map(category => (
<li
style={{
cursor: 'pointer',
listStyleType: 'none'
}}
key={category}
onClick={() => setCategory(category)}
>
<a className='dropdown-item'>{category}</a>
</li>
))}
</ul>
...
{resPerPage <= count && (
<div className="d-flex justify-content-center mt-5">
<Pagination
activePage={currentPage}
itemsCountPerPage={resPerPage}
totalItemsCount={contentsCount}
onChange={setCurrentPageNo}
nextPageText={'Next'}
prevPageText={'Prev'}
firstPageText={'First'}
lastPageText={'Last'}
itemClass="page-item"
linkClass="page-link"
/>
</div>
)}
this is the request in the backend for content and its item counts. ideally the goal is to get total count per category.
exports.getContents = asyncErrors (async (req,res,next) =>{
const contentsCount = await Content.countDocuments(); //162
console.log(contentsCount)
const resPerPage = 9;
const apiFeatures = new APIFeatures(Content.find(),req.query).search().filter().pagination(resPerPage)
let contents = await apiFeatures.query;
console.log(contents)
let filteredContentCount = contents.length;
console.log(filteredContentCount)
/* check for category request
assign total amount of objects within a category to a variable/constant
if(category.req){
let contentCount = category.counter
}
*/
setTimeout(() => {
res.status(200).json({
success:true,
contents,
contentsCount,
resPerPage,
filteredContentCount
})
}, 2000);
})
filtering and pagination:
class APIFeatures {
constructor(query,queryStr){
this.query = query;
this.queryStr = queryStr;
}
search(){
const keyword = this.queryStr.keyword ? {
name:{
$regex:this.queryStr.keyword,
$options:'i'
}
}:{}
console.log(keyword)
this.query = this.query.find({...keyword})
return this;
}
filter(){
const queryCopy = {... this.queryStr}
//removing fields from the query
// console.log(queryCopy)
const removeFields =['keyword','limit','page']
removeFields.forEach(el => delete queryCopy[el]);
// console.log(queryCopy)
//advance filter for title,etc..
let queryStr = JSON.stringify(queryCopy)
queryStr = queryStr.replace(/\b(gt|gte|lt|lte)\b/g,match=>`$${match}`)
console.log(queryCopy)
console.log(queryStr)
this.query = this.query.find(JSON.parse(queryStr));
return this;
}
pagination(resPerPage){
const currentPage = Number(this.queryStr.page) || 1;
const skip = resPerPage * (currentPage-1)
this.query = this.query.limit(resPerPage).skip(skip)
return this
}
}
module.exports = APIFeatures
main react code:
import React, { Fragment, useEffect, useState } from 'react'
import '../App.css'
import { MetaData } from './MetaData'
import { Textarea } from '#nextui-org/react';
import Pagination from 'react-js-pagination'
import ContCard from './Card/ContCard'
import { useDispatch, useSelector } from 'react-redux'
import { getContents } from '../actions/contentActions'
import { useAlert } from 'react-alert'
// import Dropdown from 'react-bootstrap/Dropdown';
import { Dropdown } from 'rsuite';
import { MDBDropdown, MDBDropdownMenu, MDBDropdownToggle, MDBDropdownItem } from 'mdb-react-ui-kit';
import ReactPaginate from 'react-paginate';
export default function Content() {
const [currentPage, setCurrentPage] = useState(1)
const dispatch = useDispatch();
const alert = useAlert()
const [category, setCategory] = useState('')
const { loading, error, contents, contentsCount, resPerPage, filteredContentCount } = useSelector(state => state.contents);
const categories = [
...
]
useEffect(() => {
if (error) {
return alert.error(error)
}
dispatch(getContents(currentPage, category));
}, [dispatch, alert, error, currentPage, category])
function setCurrentPageNo(pageNumber) {
setCurrentPage(pageNumber)
}
// const setCategories = (category)=>{
// setCategory(category)
// }
var count = contentsCount;
console.log('before:' + count)
function setCategories(category) {
// count = filteredContentCount
// console.log(' filtered:' + count)
setCategory(category)
}
console.log('after count:' + count)
return (
<Fragment>
{loading ? <h1>Loading ...</h1> : (
<Fragment>
<section id="contents" className="container mt-5">
<div className="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Categories
</button>
<ul className="dropdown-menu">
{categories.map(category => (
<li
style={{
cursor: 'pointer',
listStyleType: 'none'
}}
key={category}
// onClick={() => setCategory(category)}
onClick={() => setCategories(category)}
>
<a className='dropdown-item'>{category}</a>
</li>
))}
</ul>
</div>
<div className="row" id='cards'>
<Fragment>
<div className="col-6 col-md-9" >
{category}
<div className="row" >
{contents && contents.map(content => (
<ContCard key={content._id} content={content} col={4} />
))}
</div>
</div>
</Fragment>
</div>
</section>
{(resPerPage <= count) && (
<div className="d-flex justify-content-center mt-5">
<Pagination
activePage={currentPage}
itemsCountPerPage={resPerPage}
totalItemsCount={contentsCount}
onChange={setCurrentPageNo}
nextPageText={'Next'}
prevPageText={'Prev'}
firstPageText={'First'}
lastPageText={'Last'}
itemClass="page-item"
linkClass="page-link"
/>
</div>
)}
</Fragment>
)}
</Fragment>
)
}
I am looking to fix pagination. It disappears once you have clicked to set the category button also pagination doesnt keep track of all the possible pages per category.

useEffect runs Mutiple times and upadting the views

I have A Component (the GetSingleVideoID Component) that needs to be rendered every time the id changes
You can check the code on Github the repo Link
the Issue is that the route to get the single video I have put the views of the video to increment when ever the route hits. and then the use Effect keep incrementing the views when I am already on the Component
The getVideoById Route
// #route GET api/v1/video/:id
// #desc GET single video
// #access Private
router.get('/:id', auth, async (req, res) => {
const { id } = req.params;
try {
const videos = await Video.find().populate('channel', ['image', 'title']);
const video = await Video.findById(id).populate('channel', [
'image',
'title',
'subscribers',
'usersToNotify',
]);
if (!video) {
return res.status(404).json({ msg: 'Video not found!' });
}
video.views += 1;
const catVid = {};
video.categoryVids = [];
videos
.filter(
(vid) =>
vid.category === video.category &&
vid._id.toString() !== video._id.toString()
)
.forEach((v) => {
catVid.video = v._id;
catVid.videoTitle = v.title;
catVid.channelTitle = v.channel.title;
catVid.thumb = v.thumbnail;
catVid.views = v.views;
catVid.createdAt = v.createdAt;
video.categoryVids = [...video.categoryVids, catVid];
});
await video.save();
res.status(200).json(video);
} catch (err) {
if (err.kind == 'ObjectId') {
return res.status(400).json({ msg: 'Video not found!' });
}
console.error(err.message);
res.status(500).send('Server Error');
}
});
When I get rid of the func from the dependencies the Subscribe and Notify doesn't work perfectly anymore (like update when i click) I would have to refresh for the changes to work.
import React, { useState, useEffect, useCallback } from 'react';
import moment from 'moment';
import { useParams, Link, useNavigate } from 'react-router-dom';
import { useVideoGlobalContext } from '../actions/video';
import { useAuthGlobalContext } from '../actions/auth';
import { useChannelGlobalContext } from '../actions/channel';
import { timeSince, intToString } from '../utils/utilities';
import Section from '../components/Section';
const SingleVideo = () => {
const {
singleVideo,
loading,
getVideoByID,
commentVideo,
likeVideo,
deleteComment,
likes,
unlikes,
unlikeVideo,
deleteVideo,
} = useVideoGlobalContext();
const { user, isAuthenticated, userChannel } = useAuthGlobalContext();
const { id } = useParams();
const {
notifyChannel,
msg,
newChannel,
unnotifyChannel,
subscribeChannel,
unsubscribeChannel,
} = useChannelGlobalContext();
const [text, setText] = useState('');
const [subscribed, setSubscribed] = useState(false);
const [sub, setSub] = useState(false);
const [liked, setLiked] = useState(false);
const [like, setLike] = useState(false);
const [disliked, setDisliked] = useState(false);
const [dislike, setDislike] = useState(false);
const [notified, setNotified] = useState(false);
const [notify, setNotify] = useState(false);
const navigate = useNavigate();
useEffect(() => {
if (singleVideo) {
const isSubscribed = singleVideo.channel.subscribers.some(
(cha) => cha.user == user._id
);
setSub(subscribed);
setSubscribed(isSubscribed);
}
}, [singleVideo, subscribeChannel, unsubscribeChannel, newChannel]);
useEffect(() => {
if (singleVideo) {
const isLiked = singleVideo.likes.some((like) => like.user == user._id);
setLike(liked);
setLiked(isLiked);
}
}, [singleVideo, likeVideo, unlikeVideo, newChannel]);
useEffect(() => {
if (singleVideo) {
const isLiked = singleVideo.unlikes.some((like) => like.user == user._id);
setDislike(liked);
setDisliked(isLiked);
}
}, [singleVideo, likeVideo, unlikeVideo, newChannel]);
useEffect(() => {
if (singleVideo) {
const isNotified = singleVideo.channel.usersToNotify.some(
(cha) => cha.user == user._id
);
setNotify(notified);
setNotified(isNotified);
}
}, [singleVideo, notifyChannel, unnotifyChannel, msg]);
// // console.log(data);
useEffect(() => {
getVideoByID(id);
}, [id]);
// useEffect(() => {
// getVideoByID(id);
// }, [getVideoByID, id]);
const onSubscribe = () => {
setSub(!sub);
if (sub && subscribed) {
unsubscribeChannel(singleVideo.channel._id);
// if (msg.msg) {
// alert(msg.msg);
// }
// alert(subMsg.msg);
} else {
subscribeChannel(singleVideo.channel._id);
// if (msg.msg) {
// alert(msg.msg);
// }
// alert(subMsg.msg);
}
};
const onNotification = () => {
setNotify(!notify);
if (notify && notified) {
unnotifyChannel(singleVideo.channel._id);
// if (msg.msg) {
// alert(msg.msg);
// }
// console.log(msg);
} else {
notifyChannel(singleVideo.channel._id);
// if (msg.msg) {
// alert(msg.msg);
// }
// console.log(msg);
}
};
const likeAVideo = () => {
setLike(!liked);
if (like && liked) {
alert('You already like this');
} else {
likeVideo(singleVideo._id);
// if (msg.msg) {
// alert(msg.msg);
// }
// console.log(msg);
}
};
const commentAVideo = (e) => {
e.preventDefault();
commentVideo(singleVideo._id, text);
setText('');
};
const dislikeAVideo = () => {
setDislike(!disliked);
if (dislike && disliked) {
alert('You already unlike this');
} else {
unlikeVideo(singleVideo._id);
// if (msg.msg) {
// alert(msg.msg);
// }
// console.log(msg);
}
};
const onDelete = (vid) => {
deleteVideo(vid);
navigate('/');
};
return (
<Section nameClass='video-page'>
{singleVideo === null ? (
<h1>Loading....</h1>
) : (
<div class='v-container'>
<div class='video-player'>
<div class='vid-container'>
<video
src={`http://localhost:5000/${singleVideo.videoPath}`}
controls
></video>
{isAuthenticated &&
userChannel &&
userChannel._id === singleVideo.channel._id ? (
<div>
<Link to={`/video/edit/${singleVideo._id}`}>
<button>
<i class='fa-solid fa-pen-to-square'></i>
</button>
</Link>
<button onClick={() => onDelete(singleVideo._id)}>
<i class='fa-solid fa-trash-can'></i>
</button>
</div>
) : (
''
)}
</div>
<div class='vid-info'>
<div class='tags mb-2 primary'>
{singleVideo.tags.map((tag) => (
<span>#{tag} </span>
))}
</div>
<h3>
{singleVideo.title}{' '}
<span class='badge-cate'>{singleVideo.category}</span>
</h3>
<div class='views-date'>
<span class='views'>
{intToString(singleVideo.views)} views
</span>
<span class='date'>
{moment(singleVideo.createdAt).format('MMM Do YYYY')}
</span>
</div>
<div class='desc'>{singleVideo.description}</div>
<div class='channel-card'>
<div class='channel-info'>
<img
src={
singleVideo.channel.image
? `http://localhost:5000/${singleVideo.channel.image}`
: '/img/no-image.png'
}
alt=''
/>
<div class='name-subs'>
<h4>{singleVideo.channel.title}</h4>
<span class='subs'>
{singleVideo.channel.subscribers.length} Subscribers
</span>
</div>
</div>
<div class='btns'>
<button onClick={likeAVideo} class='thumbs'>
<i
class={`fa-solid fa-thumbs-up ${liked ? 'primary' : ''}`}
></i>
</button>
<span>{singleVideo.likes.length}</span>
<button onClick={dislikeAVideo} class='thumbs'>
<i
class={`fa-solid fa-thumbs-down ${
disliked ? 'primary' : ''
}`}
></i>
</button>
<span>{singleVideo.unlikes.length}</span>
<button
onClick={onSubscribe}
className={`sub ${subscribed ? 'disabled' : ''}`}
>
Subscribe
</button>
<button onClick={onNotification} class='bell'>
<i
className={`fa-solid fa-bell ${
notified ? 'primary' : ''
}`}
></i>
</button>
</div>
</div>
<div class='comments'>
<h4>{singleVideo.comments.length} Comments</h4>
<form class='form' onSubmit={commentAVideo}>
<div class='form-control'>
<input
type='text'
name='text'
placeholder='Add a comment...'
class='input'
value={text}
onChange={(e) => setText(e.target.value)}
/>
</div>
<button class='btn'>Add</button>
</form>
<div class='comment-container'>
{singleVideo.comments.length > 0 ? (
singleVideo.comments.map((comment) => (
<div class='comment'>
<img
src={
comment.userImage
? `http://localhost:5000/${comment.userImage}`
: '/img/photo-me.jpeg'
}
alt=''
/>
{isAuthenticated && user._id === comment.user ? (
<button
onClick={() =>
deleteComment(singleVideo._id, comment._id)
}
class='delete'
>
<i class='fa-solid fa-trash-can'></i>
</button>
) : (
''
)}
<div class='info'>
<div>
<h5>{comment.userName}</h5>
<span>
{(timeSince(comment.date) ||
timeSince(comment.date) !== undefined) &&
timeSince(comment.date)}
</span>
</div>
<p>{comment.text}</p>
</div>
</div>
))
) : (
<h3 className='text-center'>No Comments</h3>
)}
</div>
</div>
</div>
</div>
<div class='category-videos'>
<h3>
More Videos in <span class='primary'>{singleVideo.category}</span>
</h3>
<div class='category-vids'>
{singleVideo.categoryVids.length > 0 ? (
singleVideo.categoryVids.map((video) => (
<div class='cat-vid'>
<img
src={
video.thumb
? `http://localhost:5000/${video.thumb}`
: '/img/no-image.png'
}
alt=''
/>
<div class='info'>
<h4>{video.videoTitle}</h4>
<span>
{video.channelTitle} <i class='fa-solid fa-check'></i>{' '}
</span>
<div>
<span class='views'>
{intToString(video.views)} views
</span>
<span class='time'>{timeSince(video.createdAt)}</span>
</div>
</div>
</div>
))
) : (
<h4 className='text-center'>
No Videos for {singleVideo.category}...
</h4>
)}
</div>
</div>
</div>
)}
</Section>
);
};
export default SingleVideo;
on the useEffect I have put the getVideoByID function on the dependencies as you can see on the code above.

NEXTJS Conditional Statement not working inside of a map

When I compile this code, it gives me an error saying that I am not properly closing the a tag.
When I remove the conditional statement on the following code, it works fines:
{nid[index] != undefined ? (
<a target= "_blank" rel="noreferrer" href={polygonURL + Cont[index] + "/" + nid[index]}>
) : (
<a target= "_blank" rel="noreferrer" href={unkpolygonURL + Cont[index]}>
)}
import styles from "../styles/Home.module.css";
import React, { useEffect, useState, useRef } from "react";
import { useAddress, useMetamask, useDisconnect } from "#thirdweb-dev/react";
import Spinner from "./Spinner";
export default function NFT() {
const address = useAddress();
let image = "";
let total = 0;
const [title, setTitle] = useState([]);
const [Img, setImg] = useState([]);
const [Cont, setCont] = useState([]);
const [nid, setNid] = useState([]);
const[totalNFT, setTotalNFT] = useState(0);
let page = "";
const polygonURL = "https://opensea.io/assets/matic/";
const unkpolygonURL = "https://opensea.io/assets?search[query]=";
const getPolygonNFT = async (str) => {
// const api = 'https://eth-mainnet.alchemyapi.io/v2/demo/getNFTs/?owner=0xfae46f94ee7b2acb497cecaff6cff17f621c693d';
const api = `https://polygon-mainnet.g.alchemyapi.io/v2/demo/getNFTs/?owner=` + str;
const response = await fetch(api);
const data = await response.json();
console.log(data);
let total = data.totalCount;
let firsttotal = data.ownedNfts.length;
setTotalNFT(data.ownedNfts.length);
// setPage(data.pageKey);
let page = data.pageKey;
for (var i = 0; i < firsttotal; i++){
setImg(oldArray => [...oldArray, data.ownedNfts[i].media[0].gateway]);
setTitle(oldArray => [...oldArray, data.ownedNfts[i].metadata.name]);
setCont(oldArray => [...oldArray, data.ownedNfts[i].contract.address]);
setNid(oldArray => [...oldArray, data.ownedNfts[i].metadata.edition])
}
let j = true;
let count = 0;
while (j){
if (page != undefined){
const api = `https://polygon-mainnet.g.alchemyapi.io/v2/demo/getNFTs/?owner=` + str + `&pageKey=` + page;
const response = await fetch(api);
const data = await response.json();
console.log(data)
page = data.pageKey;
let nexttotal = data.ownedNfts.length;
for (var k = count; k < nexttotal+count ; k++){
setImg(oldArray => [...oldArray, data.ownedNfts[k].media[0].gateway]);
setTitle(oldArray => [...oldArray, data.ownedNfts[k].metadata.name]);
setCont(oldArray => [...oldArray, data.ownedNfts[k].contract.address]);
setNid(oldArray => [...oldArray, data.ownedNfts[k].metadata.edition])
}
} else {
j = false;
}
}
}
useEffect(() => {
getPolygonNFT(address);
}, []);
return (
<main className="inside p-10 shadow-xl shadow-gray-400/20">
{totalNFT === 0 ? (
<>
{totalNFT !== 0 ? (
<h1 className="mb-10 text-4xl font-extralight" >
<span className='text-slate-300 font-extrabold underline decoration-neutral-600'>You do not own any NFTs.</span>
</h1>
) : (
<Spinner />
)}
</>
) : (
<>
<div className="grid space-x-3 md:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4">
{title.map ( (name, index) => {
return (
<div name={index} key={index}>
{nid[index] != undefined ? (
<a target= "_blank" rel="noreferrer" href={polygonURL + Cont[index] + "/" + nid[index]}>
) : (
<a target= "_blank" rel="noreferrer" href={unkpolygonURL + Cont[index]}>
)}
<div className="flex flex-col items-center cursor-pointer transition-all duration-200 hover:scale-105">
<img className="h-80 w-80 rounded-2xl object-cover" src={Img[index]} alt=""
onError={({ currentTarget }) => {
currentTarget.onerror = null; // prevents looping
currentTarget.src="https://user-images.githubusercontent.com/24848110/33519396-7e56363c-d79d-11e7-969b-09782f5ccbab.png";
}}
/>
<div className="p-5">
<h2 className="text-gray-100 text-3xl">{title[index]}</h2>
<p className="mt-2 text-sm text-gray-400">{Cont[index]}</p>
</div>
</div>
</a>
</div>
)
})
}
</div>
</>
)
}
</main>
)}
import styles from "../styles/Home.module.css";
import React, { useEffect, useState, useRef } from "react";
import { useAddress, useMetamask, useDisconnect } from "#thirdweb-dev/react";
import Spinner from "./Spinner";
export default function NFT() {
const address = useAddress();
let image = "";
let total = 0;
const [title, setTitle] = useState([]);
const [Img, setImg] = useState([]);
const [Cont, setCont] = useState([]);
const [nid, setNid] = useState([]);
const [totalNFT, setTotalNFT] = useState(0);
let page = "";
const polygonURL = "https://opensea.io/assets/matic/";
const unkpolygonURL = "https://opensea.io/assets?search[query]=";
const getPolygonNFT = async (str) => {
// const api = 'https://eth-mainnet.alchemyapi.io/v2/demo/getNFTs/?owner=0xfae46f94ee7b2acb497cecaff6cff17f621c693d';
const api =
`https://polygon-mainnet.g.alchemyapi.io/v2/demo/getNFTs/?owner=` + str;
const response = await fetch(api);
const data = await response.json();
console.log(data);
let total = data.totalCount;
let firsttotal = data.ownedNfts.length;
setTotalNFT(data.ownedNfts.length);
// setPage(data.pageKey);
let page = data.pageKey;
for (var i = 0; i < firsttotal; i++) {
setImg((oldArray) => [...oldArray, data.ownedNfts[i].media[0].gateway]);
setTitle((oldArray) => [...oldArray, data.ownedNfts[i].metadata.name]);
setCont((oldArray) => [...oldArray, data.ownedNfts[i].contract.address]);
setNid((oldArray) => [...oldArray, data.ownedNfts[i].metadata.edition]);
}
let j = true;
let count = 0;
while (j) {
if (page != undefined) {
const api =
`https://polygon-mainnet.g.alchemyapi.io/v2/demo/getNFTs/?owner=` +
str +
`&pageKey=` +
page;
const response = await fetch(api);
const data = await response.json();
console.log(data);
page = data.pageKey;
let nexttotal = data.ownedNfts.length;
for (var k = count; k < nexttotal + count; k++) {
setImg((oldArray) => [
...oldArray,
data.ownedNfts[k].media[0].gateway,
]);
setTitle((oldArray) => [
...oldArray,
data.ownedNfts[k].metadata.name,
]);
setCont((oldArray) => [
...oldArray,
data.ownedNfts[k].contract.address,
]);
setNid((oldArray) => [
...oldArray,
data.ownedNfts[k].metadata.edition,
]);
}
} else {
j = false;
}
}
};
useEffect(() => {
getPolygonNFT(address);
}, []);
return (
<main className="inside p-10 shadow-xl shadow-gray-400/20">
{totalNFT === 0 ? (
<>
{totalNFT !== 0 ? (
<h1 className="mb-10 text-4xl font-extralight">
<span className="text-slate-300 font-extrabold underline decoration-neutral-600">
You do not own any NFTs.
</span>
</h1>
) : (
<Spinner />
)}
</>
) : (
<div className="grid space-x-3 md:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4">
{title.map((name, index) => {
return (
<div name={index} key={index}>
{nid[index] != undefined ? (
<a
target="_blank"
rel="noreferrer"
href={polygonURL + Cont[index] + "/" + nid[index]}
>
<div className="flex flex-col items-center cursor-pointer transition-all duration-200 hover:scale-105">
<img
className="h-80 w-80 rounded-2xl object-cover"
src={Img[index]}
alt=""
onError={({ currentTarget }) => {
currentTarget.onerror = null; // prevents looping
currentTarget.src =
"https://user-images.githubusercontent.com/24848110/33519396-7e56363c-d79d-11e7-969b-09782f5ccbab.png";
}}
/>
<div className="p-5">
<h2 className="text-gray-100 text-3xl">
{title[index]}
</h2>
<p className="mt-2 text-sm text-gray-400">
{Cont[index]}
</p>
</div>
</div>
</a>
) : (
<a
target="_blank"
rel="noreferrer"
href={unkpolygonURL + Cont[index]}
>
<div className="flex flex-col items-center cursor-pointer transition-all duration-200 hover:scale-105">
<img
className="h-80 w-80 rounded-2xl object-cover"
src={Img[index]}
alt=""
onError={({ currentTarget }) => {
currentTarget.onerror = null; // prevents looping
currentTarget.src =
"https://user-images.githubusercontent.com/24848110/33519396-7e56363c-d79d-11e7-969b-09782f5ccbab.png";
}}
/>
<div className="p-5">
<h2 className="text-gray-100 text-3xl">
{title[index]}
</h2>
<p className="mt-2 text-sm text-gray-400">
{Cont[index]}
</p>
</div>
</div>
</a>
)}
</div>
);
})}
</div>
)}
</main>
);
}

React question : how to pass an id from a component to another on click?

I need your help with an app that I am building. It has a forum page and I have some issues with the forum and post components.
I am trying to pass the id of the post that the user clicked on, with history.push so on the post page the id in the url that I try to get with useParams, has the value of the one I send with history.push. The purpose is for some queries I do so I show the post with its comments.
For now the layout isn’t great because I have to make this feature work.
I do not understand why it doesn’t. My console.logs show null or undefined which make no sense to me.
Thank you if you can help me with this.
Here you have two routes present in the App component. It is important for the last route, the Post one were I use :id so I can get it with useParams.
{/* Route for Trainings Wakeup Rebirth */}
<Route path='#forum' exact component={TrainingsWakeupRebirth} />
<Route path='#forum/:id' exact component={Post} />
Here you have the entire code of the Forum page. Like that you can see how I use history.push to send the value.id of the post to the Post component and the way the component itself is built.
import React, { useState, useEffect, useRef } from 'react';
import { useHistory } from 'react-router-dom';
import ReactPaginate from "react-paginate";
import Post from "../Post/Post";
import './TrainingsWakeupRebirth.scss';
import axios from "axios";
const TrainingsWakeupRebirth = (props) => {
let history = useHistory();
// const forumSectionRef = useRef();
// const postSectionRef = useRef();
const forumSection = document.getElementById('forum-block-wrapper');
const postSection = document.getElementById('post-section');
const showPost = () => {
if (forumSection.style.display === 'block') {
return forumSection.style.display = 'none',
postSection.style.display = 'block';
} else {
return forumSection.style.display = 'block',
postSection.style.display = 'none';
}
}
const [listOfPosts, setListsOfPosts] = useState([]);
const [pageNumber, setPageNumber] = useState(0);
const postsPerPage = 2;
const pagesVisited = pageNumber * postsPerPage;
const displayPosts = listOfPosts.slice(pagesVisited, pagesVisited + postsPerPage).map((value, key) => {
const forParams = () => {
return history.push(`#forum/${value.id}`);
}
const executeAll = () => {
forParams();
showPost();
if(forParams()) {
let id = value.id;
return id;
}
}
return (
<div key={key}>
<div className="topic-row" onClick={() => {executeAll()}}>
<div className="topic-title">{value.title}</div>
<div className="topic-image">
<img src={value.image} alt=""></img>
</div>
<div className="topic-message">{value.postText}</div>
</div>
</div>
);
});
const pageCount = Math.ceil(listOfPosts.length / postsPerPage);
const changePage = ({selected}) => {
setPageNumber(selected);
};
useEffect(() => {
axios.get("http://localhost:3001/posts").then((response) => {
setListsOfPosts(response.data);
});
}, []);
console.log(listOfPosts);
return (
<div className="forum" id="forum">
<div className="forum-section-wrapper page" id="forum-wrapper">
<div className="fluid-grid">
<div className="row">
<div className="col-12">
<div className="title">
<h1><span className="first-title-part">Krishna</span><span className="second-title-part">Hara</span></h1>
</div>
<div className="quote">
<span className="quote-left">FORUM</span><span className="quote-right">Eco Village</span>
</div>
</div>
</div>
<div className="row">
<div className="col-12">
<div className="forum-block-wrapper" id="forum-block-wrapper">
{displayPosts}
<ReactPaginate
previousLabel={"Previous"}
nextLabel={"Next"}
pageCount={pageCount}
onPageChange={changePage}
containerClassName={"paginationBttns"}
previousLinkClassName={"previousBttn"}
nextLinkClassName={"nextBttn"}
activeClassName={"paginationActive"}
/>
</div>
</div>
</div>
</div>
</div>
<div className="post-section" id="post-section">
<div className="fluid-grid">
<div className="row">
<div className="col-12">
<Post />
</div>
</div>
</div>
</div>
</div>
)
};
export default TrainingsWakeupRebirth;
Here is some code from the Post component, so you can see the code that should work but doesn't. Also the console.log(id)
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import axios from "axios";
const Post = (props) => {
let { id } = useParams();
const [postObject, setPostObject] = useState({});
const [comments, setComments] = useState([]);
const [newComment, setNewComment] = useState("");
console.log(id);
useEffect(() => {
axios.get(`http://localhost:3001/posts/byId/${id}`).then((response) => {
console.log(response);
setPostObject(response.data);
});
axios.get(`http://localhost:3001/comments/${id}`).then((response) => {
setComments(response.data);
});
}, [id]);
const addComment = () => {
axios.post("http://localhost:3001/comments", {
commentBody: newComment,
Postid: id,
})
.then((response) => {
const commentToAdd = { commentBody: newComment };
setComments([...comments, commentToAdd]);
setNewComment("");
});
};
console.log(postObject);
return (
<div className="post-section-wrapper">
{/* <div>
<div className="title">
{postObject.title}
</div>
<div className="image">
<img src={postObject.image}></img>
</div>
<div className="message">
{postObject.postText}
</div>
</div> */}
<div className="comments-wrapper">
<div className="">
<input
type="text"
placeholder="Comment..."
autoComplete="off"
value={newComment}
onChange={(event) => {
setNewComment(event.target.value);
}}
/>
<button onClick={addComment}> Add Comment</button>
</div>
<div className="comments-row">
{comments.map((comment) =>
(
<div key={comment.id} className="comment">
{comment.commentBody}
</div>
)
)}
</div>
</div>
</div>
);
}
export default Post;
Thank you very very much!!!
#DrewReese and #JoelHager Thank you so much for checking my code and for your advice. In the meantime I found out that we can pass to a component, aside from the pathname, other values with history.push that we retrieve by using useLocation in the component that we want to. I will answer my own question and add the code.
Here is my Forum component, I prefer adding the entire code so everything is clear. In forParams you will see how I pass the value that I need with useHistory and the attribute state and detail.
import React, { useState, useEffect, useRef } from 'react';
import { useHistory } from 'react-router-dom';
import ReactPaginate from 'react-paginate';
import Post from '../Post/Post';
import './TrainingsWakeupRebirth.scss';
import axios from 'axios';
const TrainingsWakeupRebirth = (props) => {
let history = useHistory();
// const forumSectionRef = useRef();
// const postSectionRef = useRef();
const forumSection = document.getElementById('forum-block-wrapper');
const postSection = document.getElementById('post-section');
const showPost = () => {
if (forumSection.style.display === 'block') {
return forumSection.style.display = 'none',
postSection.style.display = 'block';
} else {
return forumSection.style.display = 'block',
postSection.style.display = 'none';
}
}
const [listOfPosts, setListsOfPosts] = useState([]);
const [pageNumber, setPageNumber] = useState(0);
const postsPerPage = 2;
const pagesVisited = pageNumber * postsPerPage;
const displayPosts = listOfPosts.slice(pagesVisited, pagesVisited + postsPerPage).map((value, key) => {
const forParams = () => {
history.push(
{
pathname: `#forum#${value.id}`,
state: { detail: value.id }
}
);
}
const executeAll = () => {
forParams();
showPost();
}
return (
<div key={key} onClick={() => {executeAll()}}>
<div className="topic-row">
<div className="topic-title">{value.title}</div>
<div className="topic-image">
<img src={value.image} alt=""></img>
</div>
<div className="topic-message">{value.postText}</div>
</div>
</div>
);
});
const pageCount = Math.ceil(listOfPosts.length / postsPerPage);
const changePage = ({selected}) => {
setPageNumber(selected);
};
useEffect(() => {
axios.get("http://localhost:3001/posts").then((response) => {
setListsOfPosts(response.data);
});
}, []);
console.log(listOfPosts);
return (
<div className="forum" id="forum">
<div className="forum-section-wrapper page" id="forum-wrapper">
<div className="fluid-grid">
<div className="row">
<div className="col-12">
<div className="title">
<h1><span className="first-title-part">Krishna</span><span className="second-title-part">Hara</span></h1>
</div>
<div className="quote">
<span className="quote-left">FORUM</span><span className="quote-right">Eco Village</span>
</div>
</div>
</div>
<div className="row">
<div className="col-12">
<div className="forum-block-wrapper" id="forum-block-wrapper">
{displayPosts}
<ReactPaginate
previousLabel={"Previous"}
nextLabel={"Next"}
pageCount={pageCount}
onPageChange={changePage}
containerClassName={"paginationBttns"}
previousLinkClassName={"previousBttn"}
nextLinkClassName={"nextBttn"}
activeClassName={"paginationActive"}
/>
</div>
</div>
</div>
</div>
</div>
<div className="post-section" id="post-section">
<div className="fluid-grid">
<div className="row">
<div className="col-12">
<Post />
</div>
</div>
</div>
</div>
</div>
)
};
export default TrainingsWakeupRebirth;
In the Post component with useLocation and useEffect I get location.state.detail which is the id of the Post, that with useState I set to the constant postId.
import React, { useEffect, useState } from "react";
import { useParams, useHistory, useLocation } from "react-router-dom";
import axios from "axios";
import './Post.scss';
const Post = (props) => {
// let { id } = useParams();
const location = useLocation();
const [postId, setPostId] = useState();
useEffect(() => {
console.log(location.pathname); // result: '#id'
if(location.state) {
console.log(location.state.detail); // result: postId
setPostId(location.state.detail);
}
}, [location]);
const [postObject, setPostObject] = useState({});
const [comments, setComments] = useState([]);
const [newComment, setNewComment] = useState("");
// console.log(id);
useEffect(() => {
axios.get(`http://localhost:3001/posts/byId/${postId}`).then((response) => {
console.log(response.data);
setPostObject(response.data);
});
axios.get(`http://localhost:3001/comments/${postId}`).then((response) => {
setComments(response.data);
});
}, [postId]);
const addComment = () => {
axios.post("http://localhost:3001/comments", {
commentBody: newComment,
Postid: postId,
})
.then((response) => {
const commentToAdd = { commentBody: newComment };
setComments([...comments, commentToAdd]);
setNewComment("");
});
};
if(postObject !== null) {
console.log(postObject);
}
return (
<div className="post-section-wrapper">
{postObject !== null
?
<div className="posts-wrapper">
<div className="title">
{postObject.title}
</div>
<div className="image">
<img src={postObject.image}></img>
</div>
<div className="message">
{postObject.postText}
</div>
</div>
:
null
}
<div className="comments-wrapper">
<div className="">
<input
type="text"
placeholder="Comment..."
autoComplete="off"
value={newComment}
onChange={(event) => {
setNewComment(event.target.value);
}}
/>
<button onClick={addComment}> Add Comment</button>
</div>
<div className="comments-row">
{comments.map((comment) =>
(
<div key={comment.id} className="comment">
{comment.commentBody}
</div>
)
)}
</div>
</div>
</div>
);
}
export default Post;

React JS doesn't render on first time

import React, { useState, useRef, useEffect } from "react";
import fire from "../../../config";
import { useAuth } from "../../AuthContext";
import { Grid, Paper, Typography } from "#material-ui/core";
import "./style.css";
import { Link } from "react-router-dom";
import UserReviewComponent from "./UserReviewComponent";
import ReviewComponent from "../Reviews/ReviewComponent";
import ReactPaginate from "react-paginate";
export default function UserReviews() {
const [reviews, setReviews] = useState([]);
const [photo, setPhoto] = useState();
const [state, setstate] = useState();
const { currentUser } = useAuth();
const refItem = fire.firestore().collection("User");
const [loading, setLoading] = useState([true]);
const [vendorDetails, setVendorDetails] = useState([]);
const [error, setError] = useState(0);
const [value, setValue] = useState(0);
const [rating, setRating] = useState(0);
useEffect(() => {
fetchUserDetails();
fetchUserReviews();
}, []);
const [users, setUsers] = useState([]);
const [pageNumber, setPageNumber] = useState(0);
const usersPerPage = 2;
const pagesVisited = pageNumber * usersPerPage;
const pageCount = Math.ceil(users.length / usersPerPage);
const changePage = ({ selected }) => {
setPageNumber(selected);
};
const displayUsers = users
.slice(pagesVisited, pagesVisited + usersPerPage)
.map((v) => {
return (
<ReviewComponent
vendorid={v.vendorId}
rating={v.rating}
review={v.review}
useremail={v.useremail}
username={v.username}
userid={v.userid}
date={v.date}
id={v.id}
/>
);
});
const fetchUserReviews = () => {
fire
.firestore()
.collection("VendorReviews")
.where("useremail", "==", currentUser.email)
.get()
.then((querySnapshot) => {
const item = [];
querySnapshot.forEach((doc) => {
item.push(doc.data());
});
setReviews(item);
setUsers(reviews.slice(0, 50));
});
};
const fetchUserDetails = () => {
refItem.doc(currentUser.email).onSnapshot((doc) => {
if (doc.exists) {
setstate(doc.data().status);
setPhoto(doc.data().photourl);
} else {
console.log("No such document!");
}
});
};
// const getTotalUserRating = () => {
// let totalRating = 0;
// reviews.map((v) => {
// totalRating += v.rating;
// });
// setRating(totalRating);
// setLoading(false);
// };
// if (loading) {
// return <div className="App">Loading...</div>;
// }
return (
<>
<div className="container-1">
<div className="row">
<div className="col-md-12">
<div id="content" className="content content-full-width">
<div className="profile-1">
<div className="profile-header">
<div className="profile-header-cover"></div>
<div className="profile-header-content">
<div className="profile-header-img">
<img src={photo} alt="" />
</div>
<div className="profile-header-info">
<h4 className="m-t-10 m-b-5">
{currentUser.displayName}
</h4>
<p className="m-b-10">{state}</p>
<Link
to={`/user/${currentUser.uid}`}
className="btn btn-sm btn-info mb-2"
>
Edit Profile
</Link>
</div>
</div>
<ul className="profile-header-tab nav nav-tabs">
<li className="nav-item">
<a
href="#profile-post"
className="nav-link active show"
data-toggle="tab"
>
My Reviews
</a>
</li>
</ul>
</div>
</div>
<div className="container">
<div className="col-md-12">
<div className="offer-dedicated-body-left">
<div className="tab-content" id="pills-tabContent">
<div
className="tab-pane fade active show"
id="pills-reviews"
role="tabpanel"
aria-labelledby="pills-reviews-tab"
>
<div className="bg-white rounded shadow-sm p-4 mb-4 restaurant-detailed-ratings-and-reviews">
<h5 className="mb-1">All Ratings and Reviews</h5>
{displayUsers}
<hr />
<hr />
<a
className="text-center w-100 d-block mt-4 font-weight-bold"
href="#"
>
<ReactPaginate
previousLabel={"Previous"}
nextLabel={"Next"}
pageCount={pageCount}
onPageChange={changePage}
containerClassName={"paginationBttns"}
previousLinkClassName={"previousBttn"}
nextLinkClassName={"nextBttn"}
disabledClassName={"paginationDisabled"}
activeClassName={"paginationActive"}
/>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
}
If I edit something on the IDE and then save and then the content appears but there is nothing on the first render. Please help me out. I think its due to pagination and the array has undefined values on the first render and so it returns nothing. The pagination slices the array so that the pagination is implemented.
add following in useEffects dependency array
useEffect(() => {
fetchUserDetails();
fetchUserReviews();
}, [reviews, state, photo, users ]);

Resources