I have a React project using Tailwind CSS and I want the sidebar to take the full height below the logo and have the links to the left with a specific width, but it is not working
profile.jsx
import { useContext, useState } from "react";
import { useLocation } from "react-router-dom";
import { UserContext } from "../App";
import ProfileView from "./profileView"
function Profile() {
const location = useLocation();
const msg = location.state?.mes;
const [success, setSuccess] = useState(msg === undefined ? "" : msg);
const [cancel, setCancel] = useState(msg === undefined ? "" : "X");
const [name, setName] = useState(
msg === undefined
? "h-0"
: "h-10 flex justify-around items-center bg-green-200 text-black"
);
const { state, dispatch } = useContext(UserContext);
function handleClick() {
setSuccess("");
setCancel("");
setName("h-0");
}
return (
<>
<div className={name}>
{success}
<button onClick={handleClick}>{cancel}</button>
</div>
{state.logStatus ? (
<div className="h-full">
<ProfileView />
</div>
) : (
<div className="h-96 bg-red-200 flex justify-center items-center text-3xl font-bold">
<div>You need to login in order to view your profile!</div>
</div>
)}
</>
);
}
export default Profile;
profileView.jsx
import { Component, useContext, useEffect, useState } from "react";
import { UserContext } from "../App";
import AdminProfile from "./adminProfile";
import StudentProfile from "./studentProfile";
import TeacherProfile from "./teacherProfile";
function ProfileView() {
const { state, dispatch } = useContext(UserContext);
return (
<div className="h-full">
{state.identity.id === "admin" ? (
<AdminProfile />
) : state.identity.id === "teacher" ? (
<TeacherProfile />
) : (
<StudentProfile />
)}
</div>
);
}
export default ProfileView;
studentProfile.jsx
import { SiGoogleclassroom } from "react-icons/si";
import { FaHouseUser } from "react-icons/fa";
import { MdGrade } from "react-icons/md";
import { MdManageAccounts } from "react-icons/md";
import { Link } from "react-router-dom";
const side = [
{ title: "Class", icon: <SiGoogleclassroom />, link: "/class" },
{ title: "Dormitory", icon: <FaHouseUser />, link: "/dormitory" },
{ title: "Grade", icon: <MdGrade />, link: "/grade" },
{ title: "Account", icon: <MdManageAccounts />, link: "/account" },
];
function StudentProfile() {
return (
<div className="bg-[#2f4050] text-white box-border w-1/4 h-full">
{side.map((val, index) => {
return (
<Link to={val.link} key={index}>
<div>{val.icon}</div>
<div>{val.title}</div>
</Link>
);
})}
</div>
);
}
export default StudentProfile;
The section is not taking the full height because you haven't defined a height for the parent of the following component (which is react fragment)
<div className="h-full">
<ProfileView />
</div>
So giving a value for the height of the above component would get your job done.
Note: Since a fragment is the parent of the above component, you have to replace it with a JSX element.
<div className="h-[100vh]">
<div className="h-full">
<ProfileView />
</div>
</div>
Related
By using console.log(responseData.places) I have checked the fetching works since I am using a hook for this and seems to work fine until I setLoadedPlaces with is the method I use to update the loadedPlaces which I later use to get the values to fill the frontend part of the website.
This is the output I get from this console.log I did and the values are correct.
[{…}]
0: address: "sis se puede
busrespect: 'tu puedes',
creator: "6384e2f543f63be1c560effa"
description: "al mundial"
id: "6384e30243f63be1c560f000"
image:"https://upload.wikimedia.org/wikipedia/commons/thumb/1/10/Empire_State_Building_%28aerial_view%29.jpg/400px-Empire_State_Building_%28aerial_view%29.jpg"location: {lat: -12.086158, lng: -76.898019}
title: "Peru"
__v: 0
_id: "6384e30243f63be1c560f000"[[Prototype]]:
Objectlength: 1[[Prototype]]: Array(0)
So after this this the code I have in the frontend (SINCE the backend works properly) Let me know if you have any doubts with this logic
This is UserPlaces.js
import React, {useState, useEffect } from 'react';
import PlaceList from '../components/PlaceList';
import { useParams } from 'react-router-dom';
import { useHttpClient } from '../../shared/hooks/http-hook';
import ErrorModal from '../../shared/components/UIElements/ErrorModal';
import LoadingSpinner from '../../shared/components/UIElements/LoadingSpinner';
const UserPlaces = () => {
const {loadedPlaces, setLoadedPlaces} = useState();
const {isLoading, error, sendRequest, clearError } = useHttpClient();
const userId = useParams().userId;
useEffect(() => {
const fetchPlaces = async () => {
try {
const responseData = await sendRequest(
`http://localhost:5000/api/places/user/${userId}`
);
console.log(responseData.bus_stops)
setLoadedPlaces(responseData.bus_stops);
} catch (err) {}
};
fetchPlaces();
}, [sendRequest, userId]);
return (
<React.Fragment>
<ErrorModal error={error} onClear={clearError} />
{isLoading && (
<div className="center">
<LoadingSpinner />
</div>
)}
{!isLoading && loadedPlaces && <PlaceList items={loadedPlaces} />}
</React.Fragment>
);
};
export default UserPlaces;
This is Place-List.js
import React from 'react';
import "./PlaceList.css"
import Card from '../../shared/components/UIElements/Card'
import PlaceItem from './PlaceItem';
import Button from '../../shared/components/FormElements/Button';
const PlaceList = props => {
if (props.items.length === 0) {
return (
<div className='place-list-center'>
<Card>
<h2>No bus stops available. Be the first one to create one!</h2>
<Button to='/places/new'> Create Bus Stop </Button>
</Card>
</div>
);
}
return (
<ul className="place-list">
{props.items.map(bus_stops => (
<PlaceItem
key={bus_stops.id}
id={bus_stops.id}
image={bus_stops.image}
title={bus_stops.title}
busrespect={bus_stops.busrespect}
description={bus_stops.description}
address={bus_stops.address}
creatorId={bus_stops.creator}
coordinates={bus_stops.location}
/>
))}
</ul>
);
};
export default PlaceList;
This is PlaceItem.js
import React, { useState } from 'react';
import { useContext } from 'react';
import Card from '../../shared/components/UIElements/Card';
import Button from '../../shared/components/FormElements/Button';
import Modal from '../../shared/components/UIElements/Modal';
import Map from '../../shared/components/UIElements/Map';
import {AuthContext} from '../../shared//context/auth-context'
import "./PlaceItem.css";
const PlaceItem = props => {
const auth = useContext(AuthContext);
const [showMap, setShowMap] = useState(false);
const [showConfirmModal, setShowConfirmModal] = useState(false);
const openMapHandler = () => setShowMap(true);
const closeMapHandler = () => setShowMap(false);
const showDeleteWarningHandler = () => {
setShowConfirmModal(true);
};
const cancelDeleteHandler = () => {
setShowConfirmModal(false);
};
const confirmDeleteHandler = () => {
setShowConfirmModal(false); //when clicked close the new Modal
console.log('DELETING...');
};
return (
<React.Fragment>
<Modal show={showMap}
onCancel={closeMapHandler}
header={props.address}
contentClass="place-item__modal-content"
footerClass="place-item__modal-actions"
footer={<Button onClick={closeMapHandler}>Close </Button>}
>
<div className='map-container'>
<Map center={props.coordinates} zoom={16}/> {/* Should be props.coordinates but we writing default data for now until geocoding solved. */}
</div>
</Modal>
<Modal
show={showConfirmModal}
onCancel={cancelDeleteHandler}
header="Are you entirely sure?"
footerClass="place-item__modal-actions"
footer={
<React.Fragment>
<Button inverse onClick={cancelDeleteHandler}>
CANCEL
</Button>
<Button danger onClick={confirmDeleteHandler}>
DELETE
</Button>
</React.Fragment>
}
>
<p>
Do you want to proceed and delete this place? Please note that it
can't be undone thereafter.
</p>
</Modal>
<li className='"place=item'>
<Card className="place-item__content">
<div className='place-item__image'>
<img src={props.image} alt={props.title}/>
</div>
<div className='place-item__info'>
<h2>{props.title}</h2>
<h3>{props.address}</h3>
<p>{props.description}</p>
<p>{props.busrespect}</p>
</div>
<div className='place-item__actions'>
<Button inverse onClick={openMapHandler}> VIEW ON MAP</Button>
{auth.isLoggedIn && (<Button to={`/places/${props.id}`}> EDIT</Button> )}
{auth.isLoggedIn &&<Button danger onClick={showDeleteWarningHandler}> DELETE </Button>}
</div>
</Card>
</li>
</React.Fragment>
);
};
export default PlaceItem;
This is auth-context:
import { createContext } from "react";
export const AuthContext = createContext({
isLoggedIn: false,
userId: null,
login: () => {},
logout: () => {}});
This is is Modal.js
import React from 'react';
import ReactDOM from 'react-dom';
import Backdrop from './Backdrop';
import { CSSTransition } from 'react-transition-group';
import './Modal.css';
const ModalOverlay = props => {
const content =(
<div className={`modal ${props.className}`} style = {props.style}>
<header className={`modal__header ${props.headerClass}`}>
<h2>{props.header}</h2>
</header>
<form
onSubmit={
props.onSubmit ? props.onSubmit : event => event.preventDefault()
}
>
<div className={`modal__content ${props.contentClass}`}>
{props.children}
</div>
<footer className={`modal__content ${props.footerClass}`}>
{props.footer}
</footer>
</form>
</div>
);
return ReactDOM.createPortal(content, document.getElementById('modal-hook'));
};
const Modal = props => {
return (
<React.Fragment>
{props.show && <Backdrop onClick={props.onCancel} />}
<CSSTransition in={props.show}
mountOnEnter
unmountOnExit
timeout={200}
classNames="modal"
>
<ModalOverlay {...props}/>
</CSSTransition>
</React.Fragment>
);
};
export default Modal;
Also Trust the routing is correct since I have checked it already and I am just wondering if the logic in REACT with loadedPlaces, PlaceItema and PlaceList makes sense and it working. Let me know please. It will be really helpful.
Summary: Not getting any error but no visual data appears in the scren just the header of my website and the background (rest is empty) even though logic is functional.
const {loadedPlaces, setLoadedPlaces} = useState();
change the above line to
const [loadedPlaces, setLoadedPlaces] = useState();
I want to load a component depending in what page are the user.
Pages:
Executables
Shop
In the main screen I have a sidebar with 2 icons that i want the primary button sets the Executables Page and the second shop page.
Like having a web page with no routes and rendering components depending the user selection.
My code:
Components/Dashboard.tsx
import styled from "styled-components"
import Executable from "./Executable"
import Navbar from "./Navbar"
import { useEffect } from "react"
type EntryProps = {
section: string
}
const Dashboard = ({ section }: EntryProps) => {
var TypeElement
useEffect(() => {
if (section === "executables") {
TypeElement = (
<div className="grid">
<div className="row">
<Executable />
</div>
</div>
)
}
}, [section])
return (
<Section>
<Navbar />
{TypeElement}
</Section>
)
}
Components/Sidebar.tsx
import styled from "styled-components"
import { FaBars } from "react-icons/fa"
import { BsFileEarmarkBinary } from "react-icons/bs"
import { BiLogOut } from "react-icons/bi"
import { AiOutlineShoppingCart } from "react-icons/ai"
import { IoMdSettings } from "react-icons/io"
import { useState } from "react"
type SidebarProps = {
section: string
setSection: Function
}
const Sidebar = ({ section, setSection }: SidebarProps) => {
const [disabled, setDisabled] = useState(true)
const handleDisabled = () => setDisabled(!disabled)
return (
<Aside id="aside">
<div
className={disabled ? "brand center" : "brand"}
onClick={handleDisabled}
>
<FaBars />
</div>
<ul className="links">
<li>
<BsFileEarmarkBinary />
<span
className={disabled ? "disabled" : ""}
onClick={(e) => {
console.log("Executables")
setSection("executables")
}}
>
Executables
</span>
</li>
<li>
<AiOutlineShoppingCart />
<span
className={disabled ? "disabled" : ""}
onClick={(e) => {
e.preventDefault()
setSection("shop")
}}
>
Shop
</span>
</li>
<li>
<IoMdSettings />
<span
className={disabled ? "disabled" : ""}
onClick={setSection("settings")}
>
Settings
</span>
</li>
</ul>
<div className="logout">
<BiLogOut />
</div>
</Aside>
)
}
Pages/DashboardPage.tsx
import styled from "styled-components"
// Components
import Sidebar from "../components/Sidebar"
import Rightsidebar from "../components/Rightsidebar"
import Dashboard from "../components/Dashboard"
import { useState } from "react"
const DashboardPage = () => {
const [page, setPage] = useState("executables")
const setSection = (name: string) => {
setPage(name)
}
return (
<Div>
<Sidebar section={page} setSection={setSection} />
<Dashboard section={page} />
<Rightsidebar />
</Div>
)
}
You are changing the value of TypeElement conditionally according to the value of the section. TypeElement is not a state, so after changing the value of the TypeElement component is not rerendered, and the updated value is not showing on the UI. Here conditional rendering might be a good solution.
<Section>
<Navbar />
{section==='executables'? <Executable />: <Shop/>}
</Section>
I am implementing the function of cart. But I don't know how to add up the amount of cart. Currently, the value I chose is in the form of an array in the cart, and I need to find the sum of these values. The information of the item I chose is in the state of InCart as an array like this
{
"id": 1,
"name": "실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발",
"provider": "김영한",
"price": 88000,
"image": "https://cdn.inflearn.com/public/courses/324119/course_cover/07c45106-3cfa-4dd6-93ed-a6449591831c/%E1%84%80%E1%85%B3%E1%84%85%E1%85%AE%E1%86%B8%205%20%E1%84%87%E1%85%A9%E1%86%A8%E1%84%89%E1%85%A1%204.png",
"people" : 12400,
"free" : 4
},
I'm going to use the price in this part. I tried to make it using state total, but I think the way to make it is wrong. How can I get the sum? I'd appreciate it if you let me know thanks!
CartAside:
import React, { useState } from 'react'
import styled from 'styled-components';
const Wrap = styled.div`
position: relative;
left: 900px;
bottom: 260px;
`
function CartAside({InCart}) {
const [total, setTotal] = useState(0)
setTotal(InCart.map((product)=>{
return total += product.price
}))
return (
<Wrap>
<div id='Price1'>
<span id='Price11'>
선택상품 금액 {total}
</span>
</div>
</Wrap>
)
}
export default CartAside;
below file is parent component
RealCart:
import CartAside from './Components/CartAside';
function RealCart({InCart}) {
return (
<div>
<Top />
<Navi />
<Cart />
<CartHeader />
{InCart.map((cart)=> {
return <CartContent key={`key-${cart.id}`} cart={cart}/>
})}
<CartAside InCart={InCart}/>
</div>
)
}
export default RealCart;
and this is top component.I upload it just in case
App.js:
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Site from './Site/Site';
import Login from './Site/Login/Login';
import SiteHomepage from './SiteHomepage/SiteHomepage';
import LecturePrecise from './LecturePrecise/LecturePrecise';
import { useSelector } from 'react-redux';
import Login1 from './SiteHomepage/Login/Login1';
import { useState } from 'react';
import RealCart from './RealCart/RealCart';
function App() {
const [Inproducts,setInproducts] = useState([])
const [InCart, setInCart] = useState([]);
// const loginButton = useSelector(state => state.loginClick.isClick);
return (
<BrowserRouter>
{/* {loginButton&&<Login1 />} */}
<Routes>
<Route path='/realCart' element={<RealCart InCart={InCart} setInCart={setInCart}/>} />
<Route path='/loginHome' element={<Site InCart={InCart} setInCart={setInCart} Inproducts={Inproducts} setInproducts={setInproducts}/>}/>
<Route path='/' element={<SiteHomepage />} />
<Route path='/lecturePrecise/:id' element={<LecturePrecise/>} />
</Routes>
</BrowserRouter>
);
}
export default App;
I made map function in the file below
Section5Bottom.jsx:
import axios from 'axios';
import React, { useEffect } from 'react';
import "../section5.css";
import Section5Card from './Section5Card';
function Section5Bottom({Inproducts, setInproducts, InCart, setInCart}) {
useEffect (()=> {
axios.get("/data/products.json").then((data)=>{
setInproducts(data.data.products);
});
},[setInproducts]);
return (
<div id='Section5Bottom'>
{
Inproducts.map((product)=>{
return <Section5Card key={`key-${product.id}`} product={product} InCart={InCart} setInCart={setInCart}/>
})
}
</div>
)
}
export default Section5Bottom;
I made the array change when I click the cart icon in this file
Section5Card.jsx:
import '../section5.css';
import {FaCartPlus} from 'react-icons/fa';
import { useDispatch } from 'react-redux';
import './card.css';
function Section5Card({product, InCart, setInCart}) {
const dispatch = useDispatch();
const handleCart = () => {
const cartItem = {
id : product.id,
image : product.image,
provider : product.provider,
price : product.price,
name : product.name
}
setInCart([...InCart, cartItem])
}
return (
<div>
<div id='CardWrap'>
<div>
<img id='Section5CardImg' src={product.image} />
</div>
<div>
<FaCartPlus size='20' style={{color:'black', position:'relative', top:'124px', left:'130px', cursor:'pointer'}} onClick={()=>{dispatch({type:"ADD"}); handleCart()}} />
</div>
<div id='CardBot'>
<div id='CardBotBPrice'>
₩{product.price}
</div>
<div id='CardBotTag'>
{product.people?
<span id='CardBotTagPeople'>
{product.people}명
</span>:
<>
<span id='CardBotTagSale'>
{product.sale}
</span>
</>}
</div>
</div>
</div>
</div>
)
}
export default Section5Card;
It's better to define another method for this using Array.prototype.reduce() method. And since you need to update the total if your cart gets updated you need to use the useEffect with InCart as a dependency array.
import React, { useMemo, useState } from 'react'
import styled from 'styled-components';
const Wrap = styled.div`
position: relative;
left: 900px;
bottom: 260px;
`
function CartAside({InCart}) {
const total = useMemo(() => {
return calculateTotal(InCart);
}, [InCart])
const calculateTotal = (cartItems) => {
if(cartItems) {
return cartItems.reduce((acc, obj) => {
return acc + obj.price;
}, 0)
}else {
return 0;
}
}
return (
<Wrap>
<div id='Price1'>
<span id='Price11'>
선택상품 금액 {total}
</span>
</div>
</Wrap>
)
}
export default CartAside;
I'm using a useRef on a tag to get the width value. But when the page is loaded, ref is always null. The only time it gets the value, it's when I update my code and save it inside my IDE. This is the current code :
import { Navbar } from './Navbar';
import photo from './assets/photo.jpg';
import shoes from './assets/shoes.jpg';
import jump from './assets/jump.jpg';
import { getShoes } from './Request';
import { useQuery } from 'react-query';
import { motion } from 'framer-motion';
import { useEffect, useRef, useState } from 'react';
import { Loader } from './Loader';
export const Home = () => {
const { data, isSuccess } = useQuery('shoes', getShoes);
const [carouselMaxWidth, setCarouselMaxWidth] = useState<number>(0);
const ref = useRef<HTMLDivElement | null>(null);
useEffect(() => {
console.log(ref);
if (ref && ref.current) {
const tmp = ref.current?.scrollWidth - ref.current?.offsetWidth;
setCarouselMaxWidth(() => tmp);
}
}, []);
return (
<>
{isSuccess ? (
<div className='h-screen'>
<div ref={ref} className='overflow-x-hidden'>
<motion.div
className='flex min-w-fit space-x-5 cursor-grab'
drag='x'
dragConstraints={{ right: 0, left: -carouselMaxWidth }}
style={{ width: carouselMaxWidth }}
>
{data.slice(0, 10).map((i: any, key: number) => {
return (
<motion.div className='h-full w-96 pointer-events-none' key={key}>
<img src={i.img} alt={i.title} className='object-cover h-96 w-96' />
<div className='flex justify-between'>
<div>
<span>{i.title}</span>
<div className='text-opacity-50'>{i.source}</div>
</div>
<span>{i.price}</span>
</div>
</motion.div>
);
})}
</motion.div>
</div>
</div>
</div>
) : (
<Loader />
)}
</>
);
};
I am having issues managing the state of my navbar using useContext. Atm my app renders the menu items as soon as the menu toggle. I want this event to happen only onClick, also the button does not log the console.log message, it works only when I click directly on the link item ex:home.
So I have 2 questions.
How do I manage my navbar state to show how to hide the menu items without having to create a new component for it?
How do I fix my click event for it be triggered on either the menu button itself or/and menu items?
Below you will code snippets for App.js, Layout.js, ThemeContext.js, useTheme.js, useToggle.js, ToggleContext.js and the Navbar where the toggle context is used.
I would really appreciate some help here guys, I am junior and really kind of stuck here.
Thanks in advance to you all.
App.js
//import { data } from '../../SkillData';
import Header from './Header';
import Navbar from './Navbar';
import Skills from './Skills';
import Layout from './Layout';
function App () {
return (
<Layout startingTheme="light" startingToggle={"show"}>
<div>
<Navbar />
<Header />
<Skills />
</div>
</Layout>
);
}
export default App;
Layout.js
import React, { useContext } from "react";
import { ThemeContext, ThemeProvider } from "../contexts/ThemeContext";
import { ToggleContext, ToggleProvider } from "../contexts/ToggleContext";
function Layout ({startingTheme, startingToggle, children}) {
return (
<>
<ThemeProvider startingTheme={startingTheme} >
<ToggleProvider startingToggle={startingToggle}>
<LayoutNoToggleProvider>
</LayoutNoToggleProvider>
</ToggleProvider>
<LayoutNoThemeProvider >{children}</LayoutNoThemeProvider>
</ThemeProvider>
</>
);
}
function LayoutNoToggleProvider ({children}) {
const toggle = useContext(ToggleContext);
return (
<div className={
toggle === false ? "navbar navbar-collapsed" : "navbar navbar-collapse show"
}>
{children}
</div>
)
}
function LayoutNoThemeProvider ({ children }) {
const {theme} = useContext(ThemeContext);
return (
<div className={
theme === "light" ?
"container-fluid bg-white" :
"container-fluid bg-dark"
}>
{children}
</div>
);
}
export default Layout;
ThemeContext
import React, { createContext} from "react";
import useTheme from "../hooks/useTheme";
export const ThemeContext = createContext();
function ThemeProvider ({children, startingTheme}) {
const { theme, setTheme } = useTheme(startingTheme);
return (
<ThemeContext.Provider value={
{theme, setTheme}
}>
{children}
</ThemeContext.Provider>
);
}
export { ThemeProvider };
useTheme.js
import { useState } from "react";
function useTheme (startingTheme ="light") {
const [theme, setTheme] = useState(startingTheme);
function validateTheme (themeValue) {
if (themeValue === "dark") {
setTheme("dark");
} else {
setTheme("light");
}
}
return {
theme,
setTheme: validateTheme,
}
}
export default useTheme;
ToggleContext.js
import React, { createContext } from "react";
import useToggle from "../hooks/useToggle";
export const ToggleContext = createContext();
function ToggleProvider({ children, startingToggle }) {
const { toggle, setToggle } = useToggle(startingToggle);
return (
<ToggleContext.Provider value={{ toggle, setToggle }}>
{children}
</ToggleContext.Provider>
);
}
export { ToggleProvider };
useToggle.js
import { useState } from "react";
function useToggle (startingToggle = false) {
const [toggle, setToggle] = useState(startingToggle);
function validateShowSidebar (showSidebarValue) {
if (showSidebarValue === "show") {
setToggle("show");
} else {
setToggle("");
}
}
return {
toggle,
setToggle: validateShowSidebar,
}
}
export default useToggle;
Navbar.js
import Image from "next/image";
import styles from "../../styles/Home.module.scss"
import Logo from "../../public/Knowledge Memo.svg"
import { useContext } from "react";
import { ThemeContext } from "../contexts/ThemeContext";
import { ToggleContext } from "../contexts/ToggleContext";
import Link from 'next/link';
import { useState } from "react";
const navbarData = [
{ id: "1",
title: "home",
ref: "#home"
},
{ id:"2",
title: "Skills",
ref: "#skills"
},
{ id:"3",
title: "The List",
ref: "#theList"
},
{ id: "4",
title: "Team",
ref: "#team"
},
{ id: "5",
title: "Contact",
ref: "#contact"
},
];
function Navbar() {
const theme = useContext(ThemeContext);
const toggle = useContext(ToggleContext);
return (
<>
<nav className={
theme === "light" ?
"navbar navbar-expand-lg navbar-dark fixed-top":
"navbar navbar-expand-lg navbar-dark bg-dark fixed-top id= mainNav"}>
<div className="container d-flex flex justify-content-between">
<a className="navbar-brand h-50" href="#page-top">
<div className="navbar-brand">
<Image
src={Logo}
alt="..."
fill="#fff"
objectFit="contain"
className="h-50"
/>
</div>
</a>
<button
onClick={ () => toggle === !toggle, console.log("clicked")}
className="navbar-toggler collapsed"
type="button"
data-bs-toggle="collapsed"
data-bs-target="#navbarResponsive"
aria-controls="navbarResponsive"
aria-expanded="false"
aria-label="Toggle navigation"
>
Menu
<i className="fa fa-bars ms-1 navbar-toggler" aria-hidden="true"></i>
</button>
{toggle ?
<div className="collapsed navbar-collapse mt-2 id=navbarResponsive">
<ul className="navbar-nav text-uppercase ms-auto py-4 py-lg-0">
{navbarData.map((link,idx) => {
return (
<li key={link.id}>
<Link href={`/${link.ref}`} className="nav-item" data-index={idx} passHref>
<a className="nav-link">
{link.title}
</a>
</Link>
</li>
);
})}
</ul>
</div>
: <div className="collapse navbar-collapse show mt-2 id=navbarResponsive">
<ul className="navbar-nav show text-uppercase ms-auto py-4 py-lg-0">
{navbarData.map((link,idx) => {
return (
<li key={link.id}>
<Link href={`/${link.ref}`} className="nav-item" data-index={idx} passHref>
<a className="nav-link">
{link.title}
</a>
</Link>
</li>
);
})}
</ul>
</div>}
</div>
</nav>
</>
);
}
export default Navbar;
You can try out this implemetation with reducers to handle for you the state change with localstorage. It is not an exact implemetation of your's but you can see the flow
In the AppContext.jsx
The AppContext holds the global state of the application so that it's easier working with a single context provider and dispatching actons to specific reducers to handle state change without providing many providers.
The combinedReducers handle reducer methods to a given state component
import { useReducer, createContext, useEffect } from "react";
import userReducer from "./reducers/userReducer";
import themeReducer from "./reducers/themeReducer";
export const APP_NAME = "test_app";
//Check the localstorage or set a default state
const initialState = JSON.parse(localStorage.getItem(APP_NAME))
? JSON.parse(localStorage.getItem(APP_NAME))
: {
user: {
username: "",
email: "",
isAdmin: false,
},
theme: { dark: false },
};
//Create your global context
const AppContext = createContext(initialState);
//Create combined reducers
const combinedReducers = ({ user, theme }, action) => ({
user: userReducer(user, action),
theme: themeReducer(theme, action),
});
const AppState = ({ children }) => {
//Making it to provider state
const [state, dispatch] = useReducer(combinedReducers, initialState);
useEffect(() => {
localStorage.setItem(APP_NAME, JSON.stringify(state));
}, [state]);
return (
<AppContext.Provider value={{ state, dispatch }}>
{children}
</AppContext.Provider>
);
};
export default AppState;
export { AppContext, AppState };
The above implementation works like redux but you destructure the given state to a specific reducer to handle the state change
In this I have used localstorage to keep a persistent state because with context API on page reload the state goes. Use the useEffect hook from react and add the state in the dependency array to ensure your state is in sync
In the UserReducer.jsx
const userReducer = (state, action) => {
const { type, payload } = action;
switch (type) {
case "LOGIN":
return { ...state, ...payload };
case "LOGOUT":
return {};
default:
return state;
}
};
export default userReducer;
In the ThemeReducer.jsx
const themeReducer = (state, action) => {
const { type, payload } = action;
switch (type) {
case "DARK":
return { ...payload };
default:
return state;
}
};
export default themeReducer;
Wrapping the whole app with a single provider in the index.jsx
import reactDom from "react-dom"
import React from "react"
import App from "./App"
import "./index.css"
import AppState from "./state/AppState"
reactDom.render(
<React.StrictMode>
<AppState >
<App />
</AppState>
</React.StrictMode>,
document.getElementById("root")
)
Accessing the context from App.jsx
import { useContext } from "react";
import { AppContext } from "./state/AppState";
const App = () => {
const { state, dispatch } = useContext(AppContext);
const handleLogin = () => {
dispatch({
type: "LOGIN",
payload: {
username: "Mike",
email: "mike#gmail.com",
isAdmin: false,
},
});
};
const handleLogout = () => {
dispatch({
type: "LOGOUT",
payload: {},
});
};
return (
<div className="main-container">
<div className="container">
<p>Username: {state.user.username ? state.user.username : "Unknown"}</p>
<p>Email: {state.user.email ? state.user.email : "Unknown"}</p>
</div>
<button onClick={handleLogin}>Login</button>
<button onClick={handleLogout} style={{ background: "red" }}>
Login
</button>
</div>
);
};
export default App;
Here is my code LINK if you want to see the structure Github