How to fix scrolling issue when opening Modal from Navbar in Next.js?

162 Views Asked by At

I am fairly new to Next.js, React hooks and everything. I am wondering if I am doing this right, I am making a customizable portfolio site using Next.js, Flowbite-react, and TailwindCSS on the front end and Django on the back end.

For testing, I have set up a Navbar, that opens up modals and gets the user's input.

I want to be able to display this input in the portfolio component, which is using parallax/gsap. Inspired by Robby Leonardi.

When I click on the Modal, it gets rid of my ability to scroll down the page.

My components > Navbar.js looks like this, the {modalContent being passed in is basically separate forms in components}

import {Avatar, Dropdown, Modal, Navbar} from "flowbite-react";
import { useState } from 'react';
import EditEducation from "@/components/EditEducation";
import EditExperience from "@/components/EditExperience";
import EditSkills from "@/components/EditSkills";


export default function Nav({ setEducation, setExperience, setSkills }) {
    const [modalVisible, setModalVisible] = useState(false);
    const [modalContent, setModalContent] = useState(null);

    const openModal = (content) => {
        setModalContent(content);
        setModalVisible(true);
    };

    const closeModal = () => {
        setModalVisible(false);
        setModalContent(null);
    }

    const onEducationChange = (education) => {
        setEducation(education);
    }

    const onExperienceChange = (experience) => {
        setExperience(experience);
    }

    const onSkillsChange = (skills) => {
        setSkills(skills)
    }

    return (
        <>
            <Navbar
                fluid={true}
            >
                <div className='flex md:order-2'>
                    <Dropdown
                        arrowIcon={false}
                        inline={true}
                        label={<Avatar alt='User settings' img={null} rounded={true}/>} >
                        <Dropdown.Header>
                            <span className='block text-sm'>
                                tester
                            </span>
                            <span className='block truncate text-sm font-medium'>
                                [email protected]
                            </span>
                        </Dropdown.Header>
                        <Dropdown.Item>
                            Dashboard
                        </Dropdown.Item>
                        <Dropdown.Item>
                            Settings
                        </Dropdown.Item>
                        <Dropdown.Item>
                            Sign Out
                        </Dropdown.Item>
                    </Dropdown>
                    <Navbar.Toggle/>
                </div>
                <Navbar.Collapse>
                    <Navbar.Link href='/' active={true}>
                        Home
                    </Navbar.Link>
                    <Navbar.Link href='#' onClick={() => openModal(<EditEducation
                        onEducationChange={onEducationChange}
                        onClose={closeModal}/>)}>
                        Education
                    </Navbar.Link>
                    <Navbar.Link href='#' onClick={() => openModal(<EditExperience
                        onExperienceChange={onExperienceChange}
                        onClose={closeModal}/>)}>
                        Experience
                    </Navbar.Link>
                    <Navbar.Link href='#' onClick={() => {
                        openModal(
                            <EditSkills
                            onSkillsChange={onSkillsChange}
                            onClose={closeModal}
                            />)}}>
                        Skills
                    </Navbar.Link>
                </Navbar.Collapse>
            </Navbar>
            {modalVisible && (
                <Modal
                    id='edit_modal'
                    show={modalVisible}
                    size='md'
                    popup={true}
                    onClose={closeModal}>
                    <Modal.Header/>
                    <Modal.Body>{modalContent}</Modal.Body>

                </Modal>
            )}
        </>
    )
}

And this data is going up into my pages > index.js, then back down into the components > Portfolio.js

import React, { useState } from 'react'
import Hero from "@/components/Hero";
import Footer from "@/components/Footer";
import Portfolio from "@/components/Portfolio";
import Nav from "@/components/Nav";


function Home() {
    const [education, setEducation] = useState([]);
    const [experience, setExperience] = useState([]);
    const [skills, setSkills] = useState([]);

    return (
        <>

        <Nav
            setEducation={setEducation}
            setExperience={setExperience}
            setSkills={setSkills}
        />
        <Hero/>
        <Portfolio
            education={education}
            experience={experience}
            skills={skills}
        />
        <Footer/>

        </>
            )
}

export default Home

This is what my components > Portfolio.js looks like

import React, {useEffect, useRef} from 'react';
import { ParallaxProvider, Parallax } from 'react-scroll-parallax';
import { gsap } from 'gsap';
import ScrollTrigger from 'gsap/dist/ScrollTrigger';

const PortfolioSection = ({backgroundRef,
                              sectionRef,
                              triggerRef,
                              education,
                              experience,
                              skills}) => {
    const renderEducationContent = () => {
        return education.map((edu, index) => (
          <div key={index}>
            <h4>Degree: {edu.degree}</h4>
            <p>University: {edu.university}</p>
            <p>Year: {edu.year}</p>
          </div>
        ));
    };

    const renderExperienceContent = () => {
        return experience.map((exp, index) => (
          <div key={index}>
            <h4>Company: {exp.company}</h4>
            <p>Position: {exp.position}</p>
            <p>Duration: {exp.duration}</p>
          </div>
        ));
    };

    const renderSkillsContent = () => {
        return skills.map((skill, index) => (
          <div key={index}>
            <h4>Skill: {skill}</h4>
          </div>
        ));
    };

    const sections = [
        {title: 'Education', content: renderEducationContent()},
        {title: 'Experience', content: renderExperienceContent()},
        {title: 'Skills', content: renderSkillsContent()},
    ];

    return (
    <>
      <div className="scroll-section-outer">
        <ParallaxProvider scrollAxis="horizontal">
          <div ref={triggerRef}>
            <div ref={sectionRef} className="scroll-section-inner">
              {sections.map((section, index) => (
                <Parallax y={[-30, 30]} key={index}>
                  <div
                    ref={backgroundRef}
                    style={{
                      backgroundImage: "url('/Forest.png')",
                      backgroundSize: "cover",
                      backgroundPosition: "center",
                      width: "2000px",
                    }}
                    className="h-screen"
                  >
                    <section className="scroll-section">
                      <h3>{section.title}</h3>
                      <div>{section.content}</div>
                    </section>
                  </div>
                </Parallax>
              ))}
            </div>
          </div>
        </ParallaxProvider>
      </div>
    </>
    );

}
const Portfolio = ({ education, experience, skills }) => {
    const backgroundRef = useRef(null)
    const sectionRef = useRef(null)
    const triggerRef = useRef(null)

    gsap.registerPlugin(ScrollTrigger);

    useEffect(() => {
        const pin = gsap.fromTo(sectionRef.current, {
            translateX: 0
        }, {
            // increase and decrease based on section
            translateX: "-300vw",
            ease: "none",
            direction: 1,
            scrollTrigger: {
                trigger: triggerRef.current,
                start: "top top",
                // 2000 px at the top of the next section
                end: "2000 top",
                scrub: 0.6,
                pin: true
            }
        })

        // kills animation when the component gets unordered
        return () => {
            pin.kill()
        }
    }, {})


  return (
      <PortfolioSection
          backgroundRef={backgroundRef}
          sectionRef={sectionRef}
          triggerRef={triggerRef}
          education={education}
          experience={experience}
          skills={skills}
      />
  );
};

export default Portfolio;

Am I doing this right? Everything seems to work, except for the Modal somehow breaking my ability to scroll for some reason.

I have tried to add prevent defaults to the openmodal function, I have tried to put the form right on the main page, and that doesn't break it. For some reason, the only thing that breaks my scrolling is clicking on the Nav.Link, and opening the modal.

Also, another weird thing that is happening is when I type into the modal form, i am only able to type 1 character at a time. Once I type one character, it goes out of focus and i have to click back in to the form to type 1 character at a time.

I was thinking i'd have to look into the 'useEffect()'.

0

There are 0 best solutions below