기본 : 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가 좀 더 직관적이라고 생각해서 그렇게 한 거라고 함.

useEffect ?

React 컴포넌트가 렌더링될 때마다 특정 작업(Side effect)을 실행할 수 있도록 하는 리액트 Hook이다. 여기서 side effect란 컴포넌트가 렌더링된 후 처리되어야 하는 부수적인 효과들을 말하며, 컴포넌트가 렌더링된 이후 비동기적으로 처리되는 작업들을 일컫는다. 이를 통해 함수형 컴포넌트에서도 클래스형 컴포넌트에서 쓰던 생명주기 메소드들(ex : componentDidMount)들을 쓸 수 있게 됐다.

useEffect는 실행되면 인자로 받은 콜백을 예약해뒀다가, 컴포넌트가 렌더링되면 예약해둔 콜백을 실행한다.

 

기본적인 형태

useEffect(function, deps);

// ex
useEffect(() => {
  document.title = `총 ${count}개`;
}, [count]);

function : 실행하고자 하는 함수 즉 side effect

deps : 의존성 배열. 이 배열 안의 값이 변경되는 경우에만 side effect가 실행됨. 빈 배열을 줄 경우 해당 컴포넌트가 최초 렌더링될때만(즉 생성될 때만) side effect가 실행되고, 배열 자체를 주지 않을 경우 컴포넌트가 렌더링될때마다 side effect가 수행됨.

 

※ 여기서 말하는 컴포넌트란 useEffect가 작성된 컴포넌트를 말함

 

 

Advanced

1. deps의 종류에 따른 side effect 수행

  • deps로 뭔가 값이 들어있는 배열을 준 경우 : 컴포넌트가 마운트될때(생성될 때)와 deps배열에 작성된 값들이 변경될 때만 side effect가 실행
  • deps로 빈 배열을 준 경우 : 컴포넌트가 마운트될때만 side effect가 실행 (ComponentDidMount만 표현한 느낌)
  • deps로 아무것도 주지 않은 경우 : 컴포넌트가 마운트될때, 컴포넌트가 렌더링될 때마다 side effect가 수행됨(ComponentDidMount와 ComponentDidUpdate를 표현한 느낌)

 

※좀 더 정확히 설명하면..

  1.  컴포넌트가 마운트될 때 콜백을 예약하고 deps 리스트 안의 값들을 기억함. 이후 콜백을 실행.
  2.  state변경 등으로 컴포넌트가 리렌더링되면 useEffect가 다시 실행됨. 이 때 새로 실행되는 useEffect의 deps 리스트 안의 값들을 전에 기억해두고 있던 deps 리스트의 값들과 비교
  3.  달라진 게 있다면 새로 실행된 useEffect의 콜백을 예약하고 렌더링이 끝나면 실행함. 그러나 달라진 게 없으면 콜백을 예약하지 않음(즉 렌더링 끝나도 실행되지 않음).
  4.  때문에 deps를 빈 배열로 주면 항상 전에 기억하던 것과 같은 거니까 이후에 다시 컴포넌트가 렌더링되도 콜백을 수행하지 않음 즉 최초 마운트할 때만 실행하는 꼴이 됨. deps안에 내용물이 뭔가 있을 때, 컴포넌트가 리렌더링되도 deps값이 변한 게 없으면 콜백을 실행하지 않음 즉 deps안의 값이 변경될 경우에만 콜백(side effect)가 실행되는 꼴

 

2. 컴포넌트가 unmount될때 수행할 side effect (clean up)

: side effect로 작성한 함수에서 특정 함수를 return하게 하면 clean up기능을 사용할 수 있다. 덕분에 컴포넌트가 unmount될 시 리턴하는 함수가 실행되게 할 수 있다. useEffect를 통한 side effect는 여러 번 실행될 수 있기 때문에 메모리 누수 등을 막기 위해 이런 clean up이 필요한 경우가 많다. 참고로 컴포넌트가 unmount될 때 실행되므로 컴포넌트가 업데이트될 때(리렌더링될 때)에도 수행된다. 이 경우는 리렌더링이 먼저 된 후 clean up이 수행되고, 새로운 side effect가 실행된다. 즉 useEffect에서 clean up의 동작 순서는

 

  1. props나 state가 update됨
  2. 컴포넌트가 리렌더링됨
  3. 이전 side effect의 clean up이 수행
  4. 새로운 side effect 수행

 

이다.

 

※ clean up을 쓰는 또 다른 이유

: 클로저 때문이다. useEffect에서 side effect로 주는 함수는 자기가 생성될 때의 값을 바라보기 때문. 즉 side effect내에서 참조하는 state가 가장 최신의 state가 아닐 수도 있다. 이 때 clean up이 있다면 과거의 변수 값을 참조하던 effect가 정리되고 새로운 변수 값을 다시 참조할 수 있게 된다. 

 

 

엄청 쉽다. state를 하나 만들고, 그 값이 비동기 작업이 이루어질 땐 true로 세팅해뒀다가 비동기 작업이 끝나면 false로 세팅하게 하면 된다. 평소엔 false다가 비동기 작업을 할 때만 true가 되는 식으로!

 

const [loading, setLoading] = useState(false);

// 어떤 함수 내에서
setLoading(true);
const uploaded = await 비동기작업;
setLoading(false);

 

그 다음 loading이 true인지 false인지에 따라 다음과 같이 조건부 렌더링을 할 수 있다.

{!loading && <div>평상시</div>}
{loading && <div>로딩중</div>}

 

이를 활용해 로딩중(비동기 작업 중)일 때 로딩스피터가 빙글빙글 돌도록 만들수도 있다!

우선 로딩 여부에 따라 다음과 같이 렌더링된다고 가정하자.

 

{!loading && <div>평상시</div>}
{loading && <div className="loading"></div>}

다음과 같이 CSS를 작성해보자. animation을 활용한다!

.loading {
  /* 높이, 너비 설정 */
  width: 1.5em;
  height: 1.5em;
  /* 정사각형은 border-radius를 50프로로 하면 원모양이 됨 */
  border-radius: 50%;
  border: 3px solid grey;
  /* 원의 4분의 1은 분홍으로 */
  border-top: 3px solid pink;
  /* spin이란 애니메이션을 2초동안 일정한 속도로 수행, 이를 무한히 반복 */
  animation: spin 2s linear infinite;
}

/* spin이란 이름의 애니메이션 만들기 */
@keyframes spin {
  /* 프레임별로 지정 */
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

 

+ Recent posts