简体   繁体   中英

Correct way to use state in React

Update:

So I tried to figure it out, but couldn't. I have an onClick that triggers the toggleNavbar function.

I also have the windowResized function which checks if the browser is wider than 576. If that condition is true it checks if the navbarState is true. If both conditions are true the toggleNavbar function should be called from the windowResized function.

The issue that I'm having is that the if statement below (the one in the windowResized function) never runs, because the state doesn't update.

if (navbarState) {
  toggleNavbar()
}

Is there a way to make sure that the navbarState updates before I do the checks?

navbar.js

import React, { useState, useRef, useEffect } from "react"
import { Link } from "gatsby"
import styles from "./styling/navbar.module.less"

const Navbar = ( props ) => {

  const [navbarState, setNavbarState] = useState(false)
  const [navHeight, setNavHeight] = useState()
  const ref = useRef(null)

  useEffect(() => {
    let windowResized = () => {
      let windowWidth = window.innerWidth
      if (windowWidth > 576) {
        if (navbarState) {
          toggleNavbar()
        }
      }
    }
    window.addEventListener('resize', windowResized)

    setNavHeight(ref.current.clientHeight)
  }, [])

  let toggleNavbar = () => {
    setNavbarState((navbarState) => !navbarState)
    if (navbarState) {
      props.updateClassNames(styles.contentLeftAnimate)
    }
    else{
      props.updateClassNames(styles.contentRightAnimate)
    }
  }
    
  return (
    <nav ref={ref} id={"navigation-bar"}>
      <div className={`${styles.navLinks} ${navbarState? styles.navActive:""}`} 
      style={{top: `${navHeight}px`}}>
        {props.pages.map((page, index) => (
          <Link key={page.name} className={`${styles.navLink} ${styles.navLinkHoverEffect} ${navbarState? styles.navAnimate:""}`} 
          style={{animationDelay: `${index / 7 + 0.5}s`}} to={page.link}>
              {page.name}
          </Link>
        ))}
      </div>
      <div className={`${styles.burger} ${navbarState? styles.toggle:""}`} onClick={toggleNavbar}>
        <div className={styles.line1}></div>
        <div className={styles.line2}></div>
        <div className={styles.line3}></div>
      </div>
    </nav>
  )
}

export default Navbar

You never set navbarState to false again, also put adding event listeners in an effect is better so try this instead:

//only set the event listener on mount
useEffect(() => {
  let windowResized = () => {
    let windowWidth = window.innerWidth;

    if (windowWidth > 576) {
      // if (navbarState) { this was a stale closure
      setNavbarState(false);
      console.log('Toggle navbar - Width > 576');
    } else {//not sure if you need this else
      console.log('Window is bigger than mobile');
      //set it to true again when it goes over 576 (not sure if you need this)
      setNavbarState(true);
    }
  };
  window.addEventListener('resize', windowResized);
  //probably never happens to layout but if unmounted
  //  remove the event listener
  return () =>
    window.removeEventListener('resize', windowResized);
}, []);

The toggle is probably wrong as well if you expect navbarState to immediately change after calling setNavbarState, instead try this

setNavbarState((navbarState) => {
  const newState = !navbarState;
  if (newState) {
    props.updateClassNames(styles.contentLeftAnimate);
  } else {
    props.updateClassNames(styles.contentRightAnimate);
  }
  return newState;
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM