Bleeding edge

state를 beforeunload의 함수 내부에서 localStorage로 저장하기 본문

Javascript/React & Next

state를 beforeunload의 함수 내부에서 localStorage로 저장하기

codevil 2023. 4. 6. 18:17

고민한 이유

Single Page Application을 만들다가, 서버에서 모든 Single Page Application에 대한 초기 정보를 저장하는 것이 너무 부담되어서, Local Storage에서 초기 State를 관리하기로 하였다.

 

이 문제를 해결하기 위해 만났던 문제들

nextjs에서 localStorage is not defined

이 문제는 nextjs에서는 window가 undefined인 순간이 있을 수 있는 nextjs에서 나올 수 있는 케이스로 해결 방법은 심플하다(단지 언제 undefined 인지 시점을 지정하는게 타이트할수록 번거로울 수 있다..)

function App(){
	if(typeof window==="undefined") return
    //...localStorage에 대한 것들...
}

 

해결 방법

비슷한 케이스

import { useState } from 'react';

function App() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

리액트의 상태를 표현하기 위해서 이 케이스를 가장 많이 보여준다. 이 케이스가 작동하지 않는 이유는, useState는 클로저 처럼 상태를 관리하기 때문에 실질적으로 다른 count가 더해지는 것 같아도, 이전의 count에서 값이 더해지게 되어있다. (그리고 count는 변하지 않는다!)

state에서의 해결 방법

import {useState, useEffect, useRef} from "react"

function App(){
  const [_state, setState]= useState('default state')
  const state = useRef(null);
  const changeWord = (after) =>{
    state.current = after
    setState(state.current)
  }
  const savedState = () => {
    localStorage.setItem('to', state.current)
  }
  useEffect(() => {
    window.addEventListener("beforeunload", savedState);
    return () => {
      window.removeEventListener("beforeunload", savedState);
    };
  }, []);

  return <button onClick={()=>changeWord('changed it!')}>{state}</button>
}

 

마무리

이게 왜 이랬는지만 이해하면 쉽게 구현할 수 있다. 단지 이걸 캐치하는게 생각보다 더 오래 걸렸던 것 같다. state를 그리는 것이 아닌 경우엔 이와 같은 케이스를 생각해야겠다.