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는 뒤로가기가 가능.

 

강의를 들으면서 Ref를 통해 Input태그의 값을 받아오는 걸 보곤 했지만, 무슨 이유로 Ref를 쓰는지는 정확히 몰랐었다.

이번 기회에 정리해보자.


Ref

리액트에서 원하는 시점에 실제 DOM노드에 접근하고 싶을 때 사용하는 놈이다. 다음과 같이 Ref객체를 생성할 수 있다.

import { useRef } from 'react';

// ...생략

const inputRef = useRef();

그리고, 만들어진 Ref객체는 다음과 같이 원하는 DOM요소에 ref속성을 통해 연결시킬 수 있다.

<input type="text" name="title" ref=[inputRef} />

이 Ref라는 객체는 console.log로 찍어보면 알 수 있지만 current라는 프로퍼티를 가지며, current프로퍼티는 ref속성으로 연결된 DOM노드를 가리킨다. 즉 Ref객체의 current속성을 이용해 연결된 DOM노드에 접근하여 value등을 읽어올 수 있는 것!

 

다만 주의할 점은, DOM노드는 렌더링이 끝나야만 생기니까 Ref객체의 current값 역시 컴포넌트가 화면에 렌더링됐을 때만 존재한다는 것. 따라서 Ref를 통해 DOM노드에 접근할 때는 Ref.current를 확인한 다음 사용하도록 하자.

 

그래..이래서 Ref의 current로 인풋 태그의 값을 읽어오고 뭐 그랬던 거구나~으흠~

한마디로 Ref를 사용하면 DOM노드를 직접적으로 건드릴 수 있구나~(포커스를 준다던가..)

 

..? 근데 Ref 왜 쓰는 거지?

자바스크립트에선 querySelector로 그냥 잡아올 수 있는데 말이다.


Ref를 쓰는 이유

id, class를 통해서 바닐라JS로 DOM조작이 가능한 건 맞지만, 컴포넌트가 여러 개 생긴 경우 id나 class로 원하는 DOM요소를 가져오는 건 매우 힘들다. 이 때 Ref를 통해 원하는 DOM요소를 특정하도록 할 수 있다.

 

기본 : input태그의 value프로퍼티로 input태그의 값을 가져올 수 있다. 


다음과 같이 input태그들의 값을 담을 state를 만든다.

const [values, setValues] = useState({
  title: '',
  writer: '',
  content: '',
});

tite, writer, content는 각 input태그들이 갇는 name프로퍼티다. 편의를 위해 통일시킨 것임.

그리고 다음과 같이 handleChange함수를 만든다.

const handleChange = (e) => {
  setValues(prevValues => {
    const { name, value } = e.target;
    return {
      ...prevValues,
      [name]: value
    };
  };
};

각 input 태그들에 다음과 같이 onChange프로퍼티로 멕여준다.

<form>
  <input type="text" name="title" value={values.title} onChange={handleChange} />
  <input type="text" name="writer" value={values.writer} onChange={handleChange} />
  <input type="text" name="content" value={values.content} onChange={handleChange} />
  <button type="submit">제출</button>
</form>

리액트에서의 onChange에서는 순수JS에서의 oninput이벤트와 같다. 즉 뭔가 입력할 때마다 발생! 이로 인해 input태그의 값들과 values state는 항상 같은 값을 지니게 된다.

 

그리고 제출 버튼을 누르면 동작할 form태그의 submit이벤트에 대해선 고냥 values state의 값들 이용하면 된다. 이 방법을 배우기 전까지는 제출 버튼이 눌리면 그 때 input태그의 값들을 읽어오는 방식을 써야 된다고 생각했는데, 버튼이 눌리기 전에 위에서 한 것처럼 state에 input태그 값들을 onChange를 통해서 일치시키고 제출이 눌리면 바로 state를 활용하는 방법을 쓸 수도 있다는 걸 배웠다.

 

참고로 form태그는 submit이벤트에 대해 기본적으로 GET메소드로 input태그들의 값을 보내니까 이를 방지할 것. 방지하는 방법은 e.preventDefault()를 쓰면 됨.

 

또한 위 코드에서 input태그들에 value 프로퍼티를 딱히 줄 필욘 없음. 저게 없어도 input태그 값 가져오는 건 가능하니까. 다만 저렇게 하면 항상 state값 == input태그값이 됨 즉 input태그의 값이 제어되는 효과를 준다. 

예를 들어, input태그에서 소문자로 입력을 해도 setState에서는 state에 upper를 해서 저장해둔다면 input태그의 값들도 대문자로 알아서 바뀌는 효과를 줄 수 있음. 즉 input태그의 값이 제어되는 것! 이런 컴포넌트를 controlled component라 하고, 쓰나 안 쓰나라면 controlled component로 하는 게 좋다고 한다.

 

 

※ 참조

https://ko.reactjs.org/docs/forms.html

 

폼 – React

A JavaScript library for building user interfaces

ko.reactjs.org

 

개발을 하다가 컴포넌트가 렌더링될 때 2번 렌더링되는 현상을 목격했다. 사실 이전에 다른 플젝을 할 때도 렌더링이 두 번되는 경우를 종종 봤었는데(useEffect로 예약걸어둔 함수가 두 번 실행되는..) 이번에 이게 왜 이러지 라는 생각이 들어서 알아봤다.

 

일단 결론부터 말하자면, index.js에서 React.StrictMode가 쓰여서 그렇다. 때문에 StrictMode가 적용된 걸 주석처리해주면 2번 렌더링되는 현상을 해결할 수 있다.

 

https://ko.reactjs.org/docs/strict-mode.html#gatsby-focus-wrapper

 

Strict 모드 – React

A JavaScript library for building user interfaces

ko.reactjs.org

 

이 놈은 개발모드에서만 활성화되는 녀석으로 UI적인 렌더링은 없고, 자식들에 대한 부가적인 검사와 경고를 활성화하는 것을 통해 개발에 도움을 주는 모드라고 한다. 

위 공식문서에 쓰여있다시피, StrictMode가 적용돼있으면 두 번 렌더링을 하여 생명주기 메소드들과 관련된 쉽게 오류들을 찾을 수 있게끔 도와주는거라고 한다.

 

한마디로 두 번 렌더링되는 건 내가 오류없이 잘 코딩하고 있다면 문제될 얘기가 아니라는 뜻인 듯.

순수 JS에서, 

 

  • input 이벤트 : 사용자가 input태그에 뭔가 입력할 때마다 발생하는 이벤트. 빈 input태그에 "abcd"라는 문자열을 입력했다면 a, b, c, d를 입력할 때 각각 input이벤트가 발생한다.
  • change 이벤트 : input태그의 값이 변했을 때 발생하는 이벤트. 입력이 끝나기 전과 후를 비교해 값이 변했을 때 발생한다. input태그에 원래 있던 값이 "a"였다가 "ab"로 바뀔 때 등등. 이 때 "사용자의 입력이 끝났다"로 인식되는 건 인풋 태그의 포커스가 풀릴 때, 사용자가 Enter를 입력했을 때 등등이 있다

 

근데 React에선 사용자가 뭔가 입력할 때마다의 이벤트를 받으려면 onInput으로 하지 말고 onChange로 해야 한다. 리액트 개발자들이 onChange가 좀 더 직관적이라고 생각해서 그렇게 한 거라고 함.

+ Recent posts