React Udemy #13: Making Navigation Reusable๐Ÿ˜ˆ

JEUNGBIN HANยท2022๋…„ 12์›” 23์ผ

React-Udemy-Lecture

๋ชฉ๋ก ๋ณด๊ธฐ
6/12
post-thumbnail

Navigation Theory

Traditional

When the browser loads a new HTML document, all existing JS variables and code is dumped.
=> Doesn't really matter to JS(with onde request, user instantly sees content on the screen)

If our React app followed traditional navigation ideas, It'd take more requests to show basic content.(requeest html, js, components)

Theory of Navigation in React


JS ํ™˜๊ฒฝ์—์„œ๋Š” Navigation ํ• ๊ฒฝ์šฐ ๋ชจ๋“  ๊ฐ’๋“ค์ด ๋ฆฌ์…‹ ๋œ๋‹ค.
React๋Š” Navigation ํ• ๊ฒฝ์šฐ ์ฃผ์†Œ '/images'๋กœ ๋ฐ”๊พธ์–ด์„œ ๋ชจ๋“  compoment๋ฅผ ๋ฆฌ์…‹ํ•  ํ•„์š”์—†์ด ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋งŒ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•œ๋‹ค.

JS environment no longer being reset with navigation
This only works if the state is not defined in the component. It should be in the parent componet.

Make nagivation.js

createContext

const NavigationContext = createContext();
function NavigationProvider({children}){
  const [currentPath, setCurrentPath] = useState(window.location.pathname)
  //// we can search the pathname to use console.log
  
  useEffect(() =>{
    const handler = () =>{
      setCurrentPath(window.location.pathname);
     }
     window.addEventListener('popstate', handler);
     
     return () =>{
       window.removeEventListener('popstate', handler)
     }
  })
  
  const navigate = (to) =>{
    window.history.pushState({}, '', to);
    setCurrentPath(to)
  }
  
  return (
    <NavigationContext.Provider value={{currentPath, navigate}}>
    {children}
    </NavigationContext.Provider>
  )
}

popstate

์‚ฌ์šฉ์ž์˜ ์„ธ์…˜ ๊ธฐ๋ก ํƒ์ƒ‰์œผ๋กœ ์ธํ•ด ํ˜„์žฌ ํ™œ์„ฑํ™”๋œ ๊ธฐ๋ก ํ•ญ๋ชฉ์ด ๋ฐ”๋€” ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
์ฃผ์†Œ์™€ ํ•จ๊ป˜ ๋ฐ์ดํ„ฐ๋„ ์ €์žฅํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋งค์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฐ์ดํ„ฐ์— ๋ฐ”๋€” ํŽ˜์ด์ง€์˜ ์ •๋ณด๋“ค์„ ๋‹ด์•„๋‘๊ณ  ํด๋ผ์ด์–ธํŠธ์—์„œ ์ •๋ณด๋ฅผ ํ™œ์šฉํ•ด ์ƒˆ๋กœ์šด ํŽ˜์ด์ง€๋ฅผ ๋ Œ๋”๋งํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

How do we look at the address bar?

What part of it do we care about?
We only care about pathname "/" , "/images"
EX) google.com => pathname => "/"

window.location.pathname
// we can search the pathname to use console.log

How do we update the address bar?

window.history.pushState({}, "" , "/dropdown")
// third argument is new path we want to visit

Updates the address bar but doesn't cause a refresh
Do not full page refresh

history.pushState()
history.pushState์˜ ๊ฒฝ์šฐ ์ƒˆ๋กœ์šด ์ฃผ์†Œ๋ชฉ๋ก์„ ์ถ”๊ฐ€ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด์ „ url์˜ ์ฃผ์†Œ๊ฐ€ ๋‚จ์•„์žˆ์–ด ๋ธŒ๋ผ์šฐ์ €์˜ ๋’ค๋กœ๊ฐ€๊ธฐ ๋ฒ„ํŠผ์ด ํ™œ์„ฑํ™” ๋ฉ๋‹ˆ๋‹ค.

// ํ˜„์žฌ url์ด test๋ผ๊ณ  ๊ฐ€์ • ํ–ˆ์„ ๋•Œ
// ์ด์ „ url์€ test, ํ˜„์žฌ url์€ test/pushpage๋กœ ๋ณ€๊ฒฝ๋œ๋‹ค.
history.pushState({ data: 'testData1' }, null, '/pushpage')

pushstate(state, title, url)

์ด๋Š” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์„ธ์…˜ ์Šคํƒœ์ดํŠธ๋ฅผ ์Šคํƒ์— ์ถ”๊ฐ€ํ•œ๋‹ค๊ณ  ํ•˜๋Š”๋ฐ, ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•˜์ž๋ฉด ํ•ด๋‹น url๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

function Link({to}){
// Prop 'to' describes the path that user will go to if they click this
  const handleClick = (e) =>{
    e.preventDefault();
    // Stops the standard navigation
    console.log("User navigate to : ", to)
  }
  
  return <a onClick={handleClick} href={to}>Click</a>
  //handleClick = Detect a click
}

User clicks forward or back?
=> Window emits a 'popstate' event if the user current url was added by'pushState'
=> Just type to url will refresh full page

window.history.pushState({}, "" , "/dropdown")
// do not full page refresh
//inside console
window.addEventListener("popstate", ()=> console.log(window.location.pathname))
// /aUrl when change to /aUrl
// /bUrl when change to /bUrl

Listening to Forward and Back Clicks

const [currentPath, setCurrentPath] = useState(window.location.pathname);

  // this is only for User clicks forward or back
  // make do not refresh all the variables
  useEffect(() => {
    const handler = () => {
      setCurrentPath(window.location.pathname);
    };
    window.addEventListener("popstate", handler);

    return () => {
      window.removeEventListener("popstate", handler);
    };
  }, []);

A route component

function Route({path, children}){
  const { currentPath} = useNavigation();
  
  if(path === currentPath){
    return children
  }
  return null
}

//App.js
       <Route path="/accordion">
          <AccordionPage />
        </Route>
        <Route path="/dropdown">
          <DropdownPage />
        </Route>
function Link({to, children, className, activeClassName}){
  const {navigate , currentPath} = useNavigation();
  const classes = className(
   'text-blue-500',
   className, 
   currentPath === to && activeCCalssName
  )
  
  const handleClick =(e) =>{
    if(e.metakey || e.ctrlKey){
    //return ๋ช…๋ น๋ฌธ์€ ํ•จ์ˆ˜ ์‹คํ–‰์„ ์ข…๋ฃŒํ•˜๊ณ , ์ฃผ์–ด์ง„ ๊ฐ’์„ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์ง€์ ์œผ๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
      return 
    }
    e.preventDefault()
    navigate(to)
  }
  
  return (
    <a className={classes} href={to} onClick={handleClick}>
      {children}
    </a>
  )
}

Adding sidebar component

 function Sidebar(){
   const links =[
     { label: "Dropdown", path: "/" },
     { label: "Accordion", path: "/accordion" },
    { label: "Buttons", path: "/buttons" },
   ];
   
   const renderedLinks = links.map((link)=>{
     return (
      <Link to={link.path}>{link.label}</Link>
     )
   })
   
   return (
     <div>{renderedLinks}</div>
   )
 }

Inside App.js

function App() {
  return (
    <div>
      <Sidebar />
      <div>
profile
Web Developer

0๊ฐœ์˜ ๋Œ“๊ธ€