reactjsnext.jsreact-hookspathnextjs14

NextJS - Trying to change Navbar background and logo based on which page user is at


NextJS noob here. I'm building a site with a navbar, and I want to have a different background depending on what page the user is at. I've created some states in the Navbar and a useEffect, as well as an array of pages to use a certain background for, however, when I navigate between each page, the state seems to be a page behind, and is putting the wrong background in. The Navbar.tsx is a client component. The logo component is a child of this component.

Here is the code for the parent Navbar.tsx:

'use client';

import { useState, useEffect } from 'react';
import { usePathname } from 'next/navigation';

import NavSearch from './NavSearch';
import NavLinks from './NavLinks';
import UserSection from './UserSection';
import Logo from './Logo';
import MobileMenu from './MobileMenu';
import { UserProps } from '@/utils/types';
import { pagesWhite, pagesTransparent } from '@/utils/pages';

const Navbar = ({ user }: UserProps) => {
    const [scrollActive, setScrollActive] = useState(false);
    const pathname = usePathname();

    const [whiteLogo, setWhiteLogo] = useState(!pagesWhite.includes(pathname));
    const [bgClass, setBgClass] = useState(
        whiteLogo
            ? ' bg-black shadow-[0_13px_35px_-12px_rgba(35,35,35,0.1)] text-white'
            : ' bg-white shadow-[0_13px_35px_-12px_rgba(35,35,35,0.1)] text-foreground'
    );

    useEffect(() => {
        setWhiteLogo(!pagesWhite.includes(pathname));
        setBgClass(
            whiteLogo
                ? ' bg-black shadow-[0_13px_35px_-12px_rgba(35,35,35,0.1)] text-white'
                : ' bg-white shadow-[0_13px_35px_-12px_rgba(35,35,35,0.1)] text-foreground'
        );
        console.log(bgClass);
    }, [pathname]);

    useEffect(() => {
        if (pagesTransparent.includes(pathname)) {
            window.addEventListener('scroll', () => {
                setScrollActive(window.scrollY > 100);
            });
        } else {
            setScrollActive(true);
        }
    }, []);

    return (
        <div
            className={`fixed py-4 left-0 top-0 z-40 flex w-full items-center justify-center transition duration-500 ${
                scrollActive ? bgClass : 'bg-transparent text-white'
            }`}
        >
            <div className="container flex h-16 items-center space-x-4 sm:justify-between justify-between sm:space-x-0">
                <div className="flex">
                    <Logo scrollActive={scrollActive} whiteLogo={whiteLogo} />
                </div>
                <div className="sm:flex gap-6 md:gap-10 hidden">
                    <NavLinks />
                </div>

                <div className="sm:flex hidden items-center justify-end space-x-4">
                    <NavSearch />
                </div>
                <div className="flex items-center justify-end space-x-3 ml-5">
                    <UserSection user={user} />
                </div>
                <div className="sm:hidden gap-6 md:gap-10 flex">
                    <MobileMenu />
                </div>
            </div>
        </div>
    );
};
export default Navbar;

And my logo.tsx is:

import Link from 'next/link';
import Image from 'next/image';

import logo from '@/public/images/logo.png';
import logoBlack from '@/public/images/logo-black.png';

const Logo = ({
    scrollActive,
    whiteLogo
}: {
    scrollActive: boolean;
    whiteLogo: boolean;
}) => {

    const logoUrl = whiteLogo ? logo : scrollActive ? logoBlack : logo;

    return (
        <Link href="/" className="flex items-center space-x-2 sm:w-56">
            <Image
                src={logoUrl}
                alt="Wikibeerdia"
                className="w-[150px] sm:w-[230px]"
            />
        </Link>
    );
};

export default Logo;

How can I make this work properly?


Solution

  • In pathname useEffect, you are updating whiteLogo state and bgClass state. While setting bgClass whiteLogo state is not yet updated. There can be two approaches.

    Approach 1: Set bgClass with the same condition you are using in whiteLogo

    useEffect(() => {
        setWhiteLogo(!pagesWhite.includes(pathname));
        setBgClass(
            !pagesWhite.includes(pathname)
                ? ' bg-black shadow-[0_13px_35px_-12px_rgba(35,35,35,0.1)] text-white'
                : ' bg-white shadow-[0_13px_35px_-12px_rgba(35,35,35,0.1)] text-foreground'
        );
        console.log(bgClass);
    }, [pathname]);
    

    Approach 2: Create another useEffect with whiteLogo as dependency

    useEffect(() => {
        setWhiteLogo(!pagesWhite.includes(pathname));
    }, [pathname]);
    
    useEffect(() => {
        setBgClass(
            whiteLogo
                ? ' bg-black shadow-[0_13px_35px_-12px_rgba(35,35,35,0.1)] text-white'
                : ' bg-white shadow-[0_13px_35px_-12px_rgba(35,35,35,0.1)] text-foreground'
        );
    }, [whiteLogo]);
    

    This should resolve your issue.