Search code examples
javascriptreactjsarraysreact-hooksforeach

how can i manipulate a nav element with many nav links using forEach method in react


Below is the code that I am trying to convert.

  document.addEventListener("scroll", function() {
  const links = document.querySelectorAll(".nav-link");
  for (const l of links) l.classList.toggle('scrolling', window.scrollY > 0);
})

the thing is, I intend to change the color of all my links when the page is scrolling. That said, I could have used an if-else statement instead, but using an if-else statement would only make the color of the first link to change instead. so forEach statement is the most appropriate method.

The above code was converted to react like this as shown below.

const [isLinkScrol, setIsLinkScrol] = useState(false);

const linkScroll = setLinkScrol(
   for (const l of links) l.classList.toggle( window.scrollY > 0);
);

      useEffect(() => {

        document.addEventListener('scroll', linkScroll);


        return () => { 
            document.removeEventListener('scroll', linkScroll);
              
            };

      },[]);

return (

<ul className={`nav_links ${isNavShowing ? "show_nav" : "hide_nav"}`}>
                {
                    links.map(({name, path }, index) => {
                        return (
                            <li key={index}>
                                <NavLink to={path} className={({isActive}) => isActive ? "active-nav" : ""}  onClick={() => setIsNavShowing (prev => !prev)}>{name}</NavLink>
                            </li>
                        )   
                    })
                }
            </ul>

)

however, there seams to be an error here,

setIsLinkScrol(
   for (const l of links) l.classList.toggle( window.scrollY > 0);
);

which I have not been able to wrap my head around


Solution

  • You can't pass the setIsLinkScrol setter to addEventListener as you will set the complete Event to the state, you don't want/need that.

    Instead, make a onScoll function that will cal setState with window.scrollY.

    Then you can add a className to some element(s) if the scollValue > 0


    Small demo:

    const { useState, useEffect } = React;
    
    const Example = () => {
    
        const [scollValue, setScrollValue] = useState(0);
    
        const onScroll = (e) => setScrollValue(window.scrollY);
    
        useEffect(() => {
            document.addEventListener('scroll', onScroll);
            return () => document.removeEventListener('scroll', onScroll);
        }, [])
    
        return (
            <div>
                <h1>{'Example'}</h1>
                {[...Array(50)].map((e, i) => (
                    <span className={scollValue > 0 ? 'scrolling' : ''} key={i}>Some content</span>
                ))}
            </div>
        )
    }
    ReactDOM.render(<Example />, document.getElementById("react"));
    div { display: flex; flex-direction: column }
    .scrolling { color: orange; }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
    <div id="react"></div>


    {[...Array(50)].map((e, i) => (
        <span className={scollValue > 0 ? 'scrolling' : ''} key={i}>Some content</span>
    ))}
    

    Here I use [...Array(50)].map to create the same element 50 times
    How to repeat an element n times using JSX and Lodash

    Then we use an Conditional (ternary) Operator to set the class if needed.