firebase와 연계해서 프로젝트를 진행하던 중, 로그인이후 Main page로 이동시킬 때 로그인된 user의 uid를 같이 보내야했다. 이를 어떻게 할 수 있을까?


UseNavigate

react router에서 지원하는 Hook. 지정한 경로로 페이지를 이동시킬 수 있다. 근데 두 번째 인자로 이동시킬 페이지에 함께 보낼 데이터를 지정할 수 있다! 예시는 다음과 같다.

import { useNavigate } from 'react-router-dom'; 

// ...생략

const navigate = useNavigate();

// ...생략

const gotoMain = () => {
  navigate("/main", {
    state: {
      userId: user.uid
    }
  });
};

참고로 이렇게 데이터를 보낼 때는 두 번째 인자의 state라는 속성으로 보내야 한다고 함. 

 

UseLocation

react router에서 지원하는 Hook. useNavigate를 이용해 전송된 데이터를 받을 수 있다. 예시는 다음과 같다.

import { useLocation } from 'react-router-dom'; 

// ...생략

const location = useLocation();

// ...생략

const [userId, setUserId] = useState(
  location.state?.userId
);

location의 state프로퍼티에 내가 보낸 데이터들이 담겨있다고 보면 된다. 

 

 

 

 

참고로, Link태그를 통해서도 데이터를 보낼 수 있다고 한다. 예시는 다음과 같다.

<Link to={`/main`} state={{ test: "hello world" }} >
   test
</Link>

 

또한 참고로, useNavigate의 두 번째 인자로는 state가 아니라 replace라는 것도 보낼 수 있다.

import { useNavigate } from 'react-router-dom'; 

// ...생략

const navigate = useNavigate();

// ...생략

const gotoMain = () => {
  navigate("/main", {replace: true});
};

기본값은 false이고, true로 돼있으면 페이지가 이동된 뒤 뒤로가기를 하더라도 방금 페이지로 돌아올 수 없고, 루트로 돌아오게 된다고 한다. false는 뒤로가기가 가능.

 

state?

: 리액트에서 데이터를 다룰 때 사용하는 것으로, props가 부모 컴포넌트에서 자식 컴포넌트로 값을 전달할 때 사용하는 것이었다면 state는 컴포넌트 자기 자신이 갖는 값을 의미한다. props의 주인은 부모이지만 state의 주인은 나! 인 느낌.

 

이 state의 값이 바뀔 때마다 얘를 갖고 있는 컴포넌트들이 새로 렌더링되며, state는 다음과 같이 만들 수 있다.

import { useState } from 'React';

function Component(){
	const [num, setNum] = useState(1);
    
	return(
    	<>
        </>
	)
}

export default Component

Component란 컴포넌트에서 num이란 이름의 state를 만든 모습이다. 보통 이렇게 Destrucrturing 문법으로 작성한다. 초기엔 useState를 통해 초깃값을 할당하는데 파라미터로 준 값이 초깃값이 된다. setNum은 setter함수라고 부르며 보통 state이름 앞에 set을 붙이고 camel case로 명명한다. state를 변경하는 것은 기존에 하던 것처럼 num = 5 이런 식으로 하면 안되고, 이 setter함수를 이용해야 한다. 예를 들어 setNum(5)를 해야 num이란 state가 5로 바뀐다. 

 

 

참조형 state?

js를 공부하다보면 참조형 변수라는 걸 만나게 된다. 기본형 변수는 변수에 값을 그대로 할당하는 반면 참조형 변수는 값의 주소를 할당한다는 점(즉 참조한다)에 차이가 있다. 참조형의 예로는 객체, 배열 등이 있다.

 

예를 들어 let a = 1; 을 하면 a라는 변수를 위한 공간이 메모리의 100번지에 마련되고, 100번지에 해당하는 공간에 1이란 값이 들어간다. 즉 값이 그대로 할당되는 것. 그러나 let b = [1, 2, 3]을 하면 b라는 변수를 위한 공간이 메모리의 101번지에 마련되지만 이 101번지에 해당하는 공간에 [1, 2, 3]이 아니라 메모리 어딘가에 만들어둔 [1, 2, 3]의 주소가 할당되는 것이다.

 

즉 js에선

let b = [1, 2, 3];
let c = b;
c[2] = 5;

console.log(b);
console.log(c);

 

출력되는 두 결과가 [1, 2, 5]로 같다. 

 

암튼, 리액트도 js를 기반으로 만들어진 놈이니 state를 다룰 때 state를 배열같은 걸로 해준다면, 참조형을 다루는 것이므로 예기지 못한 상황에 직면할 수 있다.  

 

다음과 같은 state를 만들었는데, 배열을 사용하는 놈이라고 하자.

const [list, setList] = useState([]);

이 때 다음 코드를 실행한 결과는 무엇이 될까?

list.push(5)
setList(list)

당연히 이 코드를 작성한 사람이 원한 것은 list에 5를 추가하는 것인데, setter함수로만 list를 조작할 수 있으니 push를 해주고 setList를 통해 list를 다시 설정해주는 것. 그러나 이렇게 하면 list state에는 아무런 변화가 없다..!

list는 배열이니까 주소를 갖고 있는데, push하던 뭘하던 주소엔 변화가 없으니까 리액트는 상태가 바뀌었다고 판단하지 않는 것.

 

그래서 참조형 변수를 state로 활용할 때 state를 변경하고 싶다면 새로운 참조형 변수를 만들어야 한다. 위와 같은 상황에서 가장 쉬운 방법은 Spread문법을 활용하는 것.

setList([...list, 5]);

 

 

※ spread문법

펼치다 라는 뜻을 갖는 문법.

let a = [1, 2, 3, 4];가 있다고 하자. a는 배열이다. 그럼 a를 펼치면 뭐가 되는가? 1, 2, 3, 4가 된다.

...은 그런 '펼치는' 역할을 해준다.

list라는 배열이 있고 [1, 2, 3, 4]일 때, 여기에 5를 push한 새 배열을 만들고 싶다. 그러면 [...list, 5]를 해주면 된다. list를 펼친 1, 2, 3, 4가 그대로 ...list에 들어간다고 생각하면 됨.

 

객체에서도 spread문법을 활용가능하다. 다음처럼. 얘도 똑같~~이 펼치다 라는 뜻에 주목해서 보면 됨.

const obj1 = {
    name : "JSH",
    age : 24
}

const obj2 = {
   ...obj1
   gender : "man"
}

// obj2는 name, age, gender프로퍼티를 가지며 value는 각각 "JSH", 24, "man"

+ Recent posts