How do I create a portal for this navbar (React) - reactjs

This is my index.html (Where the portal is supposed to lead to)
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="navbarRoot"></div>
<div id="root"></div>
</body>
Here is my navbar component
const Navbar = () => {
const [isOpen , setIsOpen] = useState(false)
const navButtonHandler = () => {
setIsOpen(!isOpen)
}
return (
<>
<nav className='navbar'>
<span><img src={logo} alt='home' className='logo' /></span>
<div className={`menuMask ${isOpen && "open"}`} onClick={navButtonHandler}></div>
<div className={`menuContainer ${isOpen && "open"}`}>
<ul className={`navitems ${isOpen && "open"}`}>
<a href="/" className='home'>
<li>Home</li>
</a>
<a href="/" className='whoWeHelp'>
<li>Who We Help</li>
</a>
<a href="/" className='services'>
<li>Services</li>
</a>
<a href="/" className='about'>
<li>About</li>
</a>
<a href="/" className='caseStudies'>
<li>Case Studies</li>
</a>
<li>
<PrimaryButton link={'https://hadizouhbi.website'}>Client Login</PrimaryButton>
</li>
<li>
<SecondaryButton link={'https://hadizouhbi.website'}>Contact</SecondaryButton>
</li>
</ul>
</div>
<div className={`navToggle ${isOpen && "open"}`} onClick={navButtonHandler}>
<div className='bar'>
</div>
</div>
</nav>
</>
)
}
Where in the code do I use this
{ReactDom.createPortal(<Navbar />, document.getElementById('navbarRoot'))}
Am i doing something wrong? because I have no idea where to put that line however I do think the syntax for that is correct just where to put it is the issue. Any help is greatly appreciated ! I am a beginner to react

If you meant to render the whole NavBar component as a portal then return in the Navbar should be like below.
import { useState } from "react";
import ReactDOM from "react-dom";
const domNode = document.getElementById("navbarRoot");
const Navbar = () => {
const [isOpen, setIsOpen] = useState(false);
const navButtonHandler = () => {
setIsOpen(!isOpen);
};
const navContent = (
<>
<nav className="navbar">
<span>{/* <img src={logo} alt="home" className="logo" /> */}</span>
<div
className={`menuMask ${isOpen && "open"}`}
onClick={navButtonHandler}
></div>
<div className={`menuContainer ${isOpen && "open"}`}>
<ul className={`navitems ${isOpen && "open"}`}>
<a href="/" className="home">
<li>Home</li>
</a>
...
...
</ul>
</div>
<div
className={`navToggle ${isOpen && "open"}`}
onClick={navButtonHandler}
>
<div className="bar"></div>
</div>
</nav>
</>
);
return ReactDOM.createPortal(navContent, domNode);
};
Use Navbar in any other place. In spite of where you try to render it in your component tree, it will always appear in the div with navbarRoot as the id.
export default function App() {
return (
<div className="App">
<Navbar />
</div>
);
}

the code {ReactDom.createPortal(<Navbar />, document.getElementById('navbarRoot'))} goes inside the return statement.
eg:
import Navbar from "component"
function MainPage(){
...
...
return(
<>
...
{ReactDom.createPortal(<Navbar />, document.getElementById('navbarRoot'))}
</>
);
}

Related

why link doesn't work when I use getStaticProps?

I'm new in next js and I'm working on project using NextJS. I have some lots in my items page that shows lots currently(pagination/items.tsx) and I also have lotDetails page that I want it to show details of each lot using dynamic route(lotDetails\id\index.tsx).
This is the folder structure:
Now when I click the Link in Items.tsx I expect it to go to lotDetails page and pass the props, but nothing happens! (It stays on Items page!). here is Items.tsx:
import React from 'react'
import Link from "next/link"
const Items = ({currentItems}:{currentItems:any}) => {
console.log(currentItems)
// const ids=currentItems.map((el:any)=>el.id)
// const paths=ids.map((el:any)=>{params:{id:el.toString()}})
// console.log(paths)
return (
<>
<div className="container">
<div className="row">
{currentItems.map((el:any)=><div className="col-md-3 ">
//this should be linked to lotDetails page!
<Link href={{pathname:"../lotDetails/[id]",query:{id:JSON.stringify(el.id),title:el.title,image:el.image,description:el.description.toString(),rate:el.rating.rate,count:el.rating.count,price:el.price},}} as={`/lotDetails/${el.id.toString()}`}>
<div className="lot">
<div className="img-container">
<img src={el.image}/>
</div>
<div className="title">
{el.title}
</div>
<div className="price">
<span className="price-title">Price:</span>
<span>{el.price}</span>
</div>
</div>
</Link>
</div>)}
</div>
</div>
</>
)
}
export default Items;
I'm using getStaticProps and GetStaticPaths in lotDetails:
const LotDetails = (props:any) => {
const dispatch=useDispatch();
console.log(props)
const lotCount=1;
const addLots=()=>{
dispatch(AddCustomerLot({...props,lotCount:lotCount}))
}
return (
<>
<div className='container lot-details'>
<div className="row" >
<div className="col-md-6">
<div className="detail-container">
<div className="title-details"><h3>{props.title}</h3></div>
<div className="badge"><FontAwesomeIcon icon={faStar}/><span>{props.rate}</span></div>
<div className="inventory">
Inventory: <span className="count">{props.count}</span>
</div>
<div className="description-details">{props.description}</div>
<div className="price">Price: <span className="price-number">{props.price}$</span> </div>
<button className="btn btn-regist" onClick={addLots}>Add to shopping basket</button>
</div>
</div>
<div className="col-md-6"><img src={props.image} alt="" /></div>
</div>
</div>
</>
)
}
export const getStaticPaths:GetStaticPaths=async(context:any)=>{
const response= await axios.get("https://fakestoreapi.com/products")
const paths=response.data.map((el:any)=>({params:{id:el.id.toString()}}))
console.log(paths)
return{
paths,
fallback:'blocking'
}
}
export const getStaticProps:GetStaticProps=async(context:any)=>{
return{
props:
{
//recieve props
id:context.query.id,
title:context.query.title,
image:context.query.image,
description:context.query.description,
rate:context.query.rate,
count:context.query.count,
price:context.query.price
}
}
}
export default LotDetails;
when I deleted getStaticProps and getStaticPaths, the link worked! So I receive that link works perfectly and the problem is in getStaticProps and getStaticPaths. Of course I don't want to use getServerSideProps.
Update
According to julio's suggestion I changed pathName, and I also changed context.query to context.params.id:
Items:
<Link href={{pathname:`/lotDetails/${el.id}`,query:{id:JSON.stringify(el.id),title:el.title,image:el.image,description:el.description.toString(),rate:el.rating.rate,count:el.rating.count,price:el.price},}} as={`/lotDetails/${el.id.toString()}`}>
<div className="lot">
<div className="img-container">
<img src={el.image}/>
</div>
<div className="title">
{el.title}
</div>
<div className="price">
<span className="price-title">Price:</span>
<span>{el.price}</span>
</div>
</div>
</Link>
lotDetails:
return (
<>
<div className='container lot-details'>
<div className="row" >
<div className="col-md-6">
//I removed all section which used props and querys using comments
{/* <div className="detail-container">
<div className="title-details"><h3>{props.title}</h3></div>
<div className="badge"><FontAwesomeIcon icon={faStar}/><span>{props.rate}</span></div>
<div className="inventory">
Inventory: <span className="count">{props.count}</span>
</div>
<div className="description-details">{props.description}</div>
<div className="price">Price: <span className="price-number">{props.price}$</span> </div>
<button className="btn btn-regist" onClick={addLots}>Add to shopping basket</button>
</div> */}
</div>
{/* <div className="col-md-6"><img src={props.image} alt="" /></div> */}
</div>
</div>
</>
)
}
export const getStaticPaths:GetStaticPaths=async(context:any)=>{
const response= await axios.get("https://fakestoreapi.com/products")
const paths=response.data.map((el:any)=>({params:{id:el.id.toString()}}))
console.log(paths)
return{
paths,
fallback:'blocking'
}
}
export const getStaticProps:GetStaticProps=async(context:any)=>{
return{
props:
{
id:context.params.id,
// title:context.query.title,
// image:context.query.image,
// description:context.query.description,
// rate:context.query.rate,
// count:context.query.count,
// price:context.query.price
}
}
}
Finally, I solved problem in two steps:
1-I'm using google chrome browser, so I configured chrome and added my localhost port:
I used this link: chrome://inspect/, clicked configure button and added my localhost port(in my case localhost:3000)
2-I added this code to lotDetails page(the page which I used axios)
axios.defaults.httpsAgent=new https.Agent({
rejectUnauthorized:false,
})
don't forget to import:
import https from "https"
Now It's working.

How can I use ref for another component?

So I have a sidebar with list of items, and when I click on an item, it should scroll to a certain div, which are outside components. Sidebar looks like this:
Sidebar component:
const Sidebar = () => {
const [sideBar, setSidebar] = useState(false);
return (
<div className="sidebar">
<span class="btn" onClick={() => setSidebar(!sideBar)}>Menu</span>
<div className="profile">
<img src={spike}/>
<span>Alim Budaev</span>
<span>Available for work</span>
</div>
<ul className="sidebarlist" id={sideBar ? "hidden" : ""}>
{SlidebarData.map((val,key) =>{
return (
<li
className="row"
id={window.location.pathname === val.link ? "active" : ""}
key={key}
onClick={()=> {
}}>
{""}
<div>
{val.title}
</div>
</li>
);
})}
</ul>
</div>
);
}
Components in App.js:
function App() {
return (
<div className="App">
<div className="header">
<Sidebar/>
<Hero">
<Particles/>
</Hero>
<About/>
<Service/>
<Form/>
<Footer/>
</div>
</div>
);
}
So I'm looking to way to scroll to a certain component when I click on . I know it can be made through useRef(), but I don't know how to do it in the Sidebar with outside components.

How to anchor a component with prop?

I have a website that I used fixed menu options to anchor to the place I wanted ,below is a simple structure to demonstrate
mypage.jsx
<div className="mywork">
<TopbarWork menuOpen={menuOpen} setMenuOpen={setMenuOpen}/>
<MenuWork menuOpen={menuOpen} setMenuOpen={setMenuOpen}/>
<div className="sections">
<Product /> //origin
<Story workData={workData}/> //what I want to achieve
<Userflow workData = {workData}/>
<System workData={workData}/>
<FinalDesign workData={workData}/>
</div>
</div>
menu.jsx(click to go to the different anchors)
import './menu.scss'
export default function Menu({menuOpen,setMenuOpen}) {
return (
<div className={"menu " + (menuOpen && "active")}>
<ul>
<li onClick={()=>setMenuOpen(false)}>
Back
</li>
<li onClick={()=>setMenuOpen(false)}>
product
</li>
</ul>
</div>
)
}
Product.jsx(one of the components)
import { CheckCircle } from '#material-ui/icons';
import './product.scss'
export default function Product({workData}) {
return (
<div className="product" id="product"> //important
<div className="productLeft">
<div className="productIntro">
<p className="title">
{workData.title}
</p>
<p className="desc">
{workData.desc}
</p>
{workData.featured.map((feature)=>{
return(
<div className="featured">
<CheckCircle className="featuredIcon"/>
{feature.title}
</div>
)
})}
</div>
</div>
<div className="productRight">
<div className="item">
<img src="assets/desktop.jpeg" alt="" className="desktopImg" />
<img src={workData.productImg} alt="" className="productImg" />
</div>
</div>
</div>
)
}
So right now the problem is I have to pass "workData" into components , but after that if I click on the anchor, it would show Cannot read properties of undefined (reading 'workData') , I assume that I have to pass the workData too when I click on the anchor ?
But what's the right way to do that ?
EDIT
I tried using Link and history (react-router-dom) but failed , here's my code
menu.jsx
export default function MenuWork({menuOpen,setMenuOpen,workData}) {
console.log(workData)
const history = useHistory();
const handleClick = ()=>{
setMenuOpen(false)
history.push({
pathname:'/mywork#userflow',
state: { workData:workData}
})
}
return (
<div className={"menu " + (menuOpen && "active")}>
<ul>
<li onClick={()=>setMenuOpen(false)}>
Back
</li>
<li onClick={()=>setMenuOpen(false)}>
<Link to="#product">Product</Link>
</li>
<li onClick={()=>setMenuOpen(false)}>
<Link to={{
pathname:"/mywork#story",
state:{ workData:workData}
}}>Story</Link>
</li>
<li onClick={()=>handleClick()}>
userflow
</li>
</ul>
</div>
)
}
If I use Link and pass the state, it will return blank page without loading anything, and If I don't pass any state with Link , it will shows the same error as above.

React 'toggleBurgerMenu' is not defined no-undef

I am a total newbie on react. I am trying to use a functional app and wanted to use the function toggleBurgerMenu(). The problem, I am not sure what else I need to define it. Should I use props.toggleBurgerMenu()?
import React from 'react'
import { Link } from "react-router-dom";
import './Header.scss'
const Header = (props) => {
toggleBurgerMenu = () => {
document.querySelector('.navbar-menu').classList.toggle('is-active');
}
return (
<div>
<header>
<nav className="navbar" role="navigation" aria-label="main navigation">
<div id="navbarBasicExample" className="navbar-menu">
<div className="navbar-start">
<Link to="/home" className="navbar-item" onClick={()=> this.toggleBurgerMenu()}>Home</Link>
</div>
</div>
</nav>
</header>
</div>
)
}
export default Header
Appreciate any help. thank you
You need to define your toggleBurgerMenu function like this const toggleBurgerMenu = () => { document.querySelector('.navbar-menu').classList.toggle('is-active'); }. And then you can use it in the onClick Event like this <Link to="/home" className="navbar-item" onClick={toggleBurgerMenu}>Home</Link>
with react the best thing to do is not using document you could use a ref instead, but in this case you can add some state and update the classname
here is an easy example
const Header = (props) => {
/** function to set the state of the button */
const [isActive, setIsActive] = useState(initialState)
return (
<div>
<header>
<nav className="navbar" role="navigation" aria-label="main navigation">
{/**if its true we add is-active class there are several package to this*/}
<div id="navbarBasicExample" className={`navbar-menu ${isActive ? 'is-active' : ''}`}>
<div className="navbar-start">
<Link to="/home" className="navbar-item" onClick={() => setIsActive(prev => !prev.setIsActive)}>Home</Link>
</div>
</div>
</nav>
</header>
</div>
)
}
export default Header

ReactJS Preload Navigation Elements

I recently moved my static navigation bar from being a general html element to included in my React rendering for my page because I wanted to incorporate the ability to dynamically load notifications in a modal that can get triggered in the navigation. With this change, I have noticed that my navigation bar does not appear immediately when the page is loaded, but when componentDidMount() { this.fetchList(); } finishes loading.
I personally belief that this is because the navigation component is being set in the render() call involved with this API fetch and since this class is being set after the call is made, then the navigation will have to wait until the fetch comes back successfully or as a failure.
If this is true, does that mean that I need to set my navigation at a higher level to ensure it loads when the page loads with styling and non-react elements?
Here is my ReactDOM.render():
import React from 'react';
import ReactDOM from 'react-dom';
import AnnotationFeedContainer from './components/app/activity-feed/activity-feed.js';
ReactDOM.render(<AnnotationFeedContainer />, document.getElementById('annotation-card'));
Here is <AnnotationFeedContainer /> which is rendering my react elements (<Navigation /> is the component I am looking to load before and regardless of fetchList()):
import React from 'react';
import fetch from 'node-fetch';
import path from 'path';
import Navigation from '../navigation';
import AnnotationSearchForm from './annotation-search-form';
import OnboardingInformation from './onboarding/information';
import ActivityFeedNotifications from './notifications/notifications';
import AnnotationFeed from './annotation-card/annotation-card-feed';
import { API_ROOT } from '../config/api-config';
//GET /api/test and set to state
export default class AnnotationFeedContainer extends React.Component{
constructor(props, context) {
super(props, context);
this.state = this.context.data || window.__INITIAL_STATE__ || { annotations: [], isLoading: true, onboardingWelcome: false, notifications: [] };
}
fetchList() {
fetch(`${API_ROOT}` + '/api' + window.location.search, { compress: false })
.then(res => {
return res.json();
})
.then(data => {
console.log(data);
this.setState({ annotations: data.annotation, user: data.user, csrf: data.csrfToken, isLoading: false, onboardingWelcome: data.onboardingWelcome, notifications: data.notifications, feedPreference: data.feedPreference });
})
.catch(err => {
console.log(err);
});
}
componentDidMount() {
this.fetchList();
}
render() {
if(this.state.feedPreference === 1){
return (
<div>
<Navigation notifications={this.state.notifications}/>
<AnnotationSearchForm />
<div className="activity-feed-container">
<div className="container">
<OnboardingInformation onboarding={this.state.onboardingWelcome}/>
<LoadingIndicator loading={this.state.isLoading} />
<div className="row">
<div className="col-md-12">
<AnnotationFeed {...this.state} />
</div>
</div>
</div>
</div>
</div>
)
} else {
return (
<div className="activity-feed-container">
<div className="container">
<OnboardingInformation onboarding={this.state.onboardingWelcome}/>
<LoadingIndicator loading={this.state.isLoading} />
<div className="row">
<div className="col-md-6 col-md-offset-3">
<AnnotationFeed {...this.state} />
</div>
<div className="col-md-1 col-md-offset-1">
<ActivityFeedNotifications notifications={this.state.notifications} />
</div>
</div>
</div>
</div>
)
}
}
};
//Loading Indicator
const LoadingIndicator = props => {
if(props.loading == true){
return (
<div className="spinner">
<div className="bounce1"></div>
<div className="bounce2"></div>
<div className="bounce3"></div>
<p>Loading...</p>
</div>
)
} else {
return null;
}
}
Navigation Component:
import React from 'react';
import NotificationPopover from './activity-feed/notifications/notifications-popover';
//Navigation
export default class Navigation extends React.Component {
render() {
return (
<nav className="navbar">
<div className="container-fluid">
<div className="navbar-header">
<button type="button" className="navbar-toggle" data-toggle="collapse" data-target="#navigationLinks">
<span className="icon-bar mobile-nav-toggle"></span>
<span className="icon-bar mobile-nav-toggle"></span>
<span className="icon-bar mobile-nav-toggle"></span>
</button>
<a className="navbar-brand" href="/app"><img src="/images/synotate_logo.svg" className="nav-logo-svg"></img></a>
</div>
<div className="collapse navbar-collapse" id="navigationLinks">
<ul className="nav navbar-nav">
<li className="nav-item">
<a className="nav-link" href="/app">Activity Feed</a>
</li>
<li className="nav-item">
<a className="nav-link" href="/app/settings">Settings</a>
</li>
</ul>
<ul className="nav navbar-nav navbar-right">
<li className="nav-item">
<NotificationPopover notifications={this.props.notifications}/>
</li>
<li className="nav-item">
<a className="nav-link" href="/app/logout">Log Out</a>
</li>
</ul>
</div>
</div>
</nav>
)
}
}
in your <AnnotationFeedContainer> you have this line if(this.state.feedPreference === 1) in the render() method.
For that condition wont be true unless you have a successful fetch() event coming from fetchList() in componentDidMount(), and if this condition returns true, you will render what ever is inside the braces of it, which includes the <Navigation> component
Else-wise you will render another code, which is loading indicator I guess, and in here, you didnt include your Navigation component, thus it won't show.
That is the logic you are using, you are telling your app to not include the Navigation component unless it fetches ur data, which happens to be logically fine!
if you want to display it other wise, you may wanna be moving it out of the if statement you have
It seems like you fetch call is responsible for setting the feedPreference variable in your state.
Since this variable is used in your if condition, and the <Navigation/> component isnt rendered when the feedPreference isn't set.
A simple solution would be to add <Navigation/> into the else condition (in the <AnnotationFeedContainer/>'s render function) :
} else {
return (
<>
<Navigation notifications={this.state.notifications}/>
<div className="activity-feed-container">
<div className="container">
<OnboardingInformation onboarding={this.state.onboardingWelcome}/>
<LoadingIndicator loading={this.state.isLoading} />
<div className="row">
<div className="col-md-6 col-md-offset-3">
<AnnotationFeed {...this.state} />
</div>
<div className="col-md-1 col-md-offset-1">
<ActivityFeedNotifications notifications={this.state.notifications} />
</div>
</div>
</div>
</div>
</>
)
}
A more "React-like" way of doing it could be to replace your entire condition wiht the following :
return (
<>
<Navigation notifications={this.state.notifications} />
{this.state.feedPreference === 1 && <AnnotationSearchForm />}
<div className="activity-feed-container">
<div className="container">
<OnboardingInformation onboarding={this.state.onboardingWelcome} />
<LoadingIndicator loading={this.state.isLoading} />
<div className="row">
{this.state.feedPreference === 1 ?
<>
<div className="col-md-6 col-md-offset-3">
<AnnotationFeed {...this.state} />
</div>
<div className="col-md-1 col-md-offset-1">
<ActivityFeedNotifications notifications={this.state.notifications} />
</div>
</>
:
<div className="col-md-12">
<AnnotationFeed {...this.state} />
</div>
}
<div className="col-md-6 col-md-offset-3">
<AnnotationFeed {...this.state} />
</div>
<div className="col-md-1 col-md-offset-1">
<ActivityFeedNotifications notifications={this.state.notifications} />
</div>
</div>
</div>
</div>
</>
)
Using inline ifs (&&) allows you to avoid repetitions. If you cannot use fragments (<> they were added in the latest React version) you can replace them with <div> tags

Resources