state, props, ref (With Typescript)

sham·2022년 1월 6일
0

Betti 개발일지

목록 보기
14/14

state

리액트의 변수 개념. 컴포넌트를 구성하는 값 중 변경될 수 있는 값을 의미하며 state가 변경된다면 해당 state와 연관된 컴포넌트는 리렌더링을 하게 된다.

const Test = () => {
  let data = 1;
  useEffect(() => {
    console.log('rerendered!');
  }, [data]);
  const plus = () => {
    data += 1;
  };
  return (
    **<>
      <div>{data}</div>
      <button onClick={plus}>+1</button>
    </>
  );
};**

만약 let, const 같이 자바스크립트의 변수를 동적 데이터로 넣게 된다면?

위의 코드를 실행하보면 버튼을 눌러도 화면은 변하지 않는 것을 확인할 수 있다.

리렌더링의 기준인 state가 변하지 않았기 때문이다.

const TestPage = () => {
  const [data, setData] = useState(0);
  useEffect(() => {
    console.log('rerendered!');
  }, [data]);
  const plus = () => {
    setData(data + 1);
  };
  return (
    <>
      <div>{data}</div>
      <button onClick={plus}>+1</button>
    </>
  );
};

리액트에서 state는 useState를 통해 할당받아 사용할 수 있다.
위의 코드를 실행해보면 버튼을 누를 때 마다 화면이 리렌더링 되는 것을 확인할 수 있다.

state은 얕은 복사로 변하지 않는다

const TestPage = () => {
  const [data, setData] = useState({ data: 0 });
  useEffect(() => {
    console.log('rerendered!');
  }, [data]);
  const plus = () => {
    data.data += 1;
    console.log(data.data);
    setData(data);
  };
  return (
    <>
      <div>{data.data}</div>
      <button onClick={plus}>+1</button>
    </>
  );
};

state가 객체 형태로 되어 있을 경우 얕은 복사로 값을 변경하면 state가 변경되었다고 판단하지 않고 리렌더링 역시 하지 않는다.

const TestPage = () => {
  const [data, setData] = useState({ data: 0 });
  useEffect(() => {
    console.log('rerendered!');
  }, [data]);
  const plus = () => {
    data.data += 1;
    console.log(data.data);
    setData(prev => {
      return { ...prev };
    });
  };
  return (
    <>
      <div>{data.data}</div>
      <button onClick={plus}>+1</button>
    </>
  );
};

위처럼 구조 분해 할당으로 기존의 객체와는 다른 주소의 객체를 리턴해주어야 state가 변경되었다고 판단을 하고 리렌더링을 한다.

구조분해 할당이란?

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment

props

부모 컴포넌트가 자식 컴포넌트로 전달하는 state, 자식 컴포넌트가 부모 컴포넌트로부터 받는 state를 props라고 한다.

const Parents = () => {
  return <Childs data={'hello, world!'} />;
};
const Childs = (props) => {
  const { data } = props;
  useEffect(() => {
    console.log('rerendered!');
  }, []);
  return <div>{data}</div>;
};

props도 state이기 때문에 부모로부터 받는 props가 변경되어도 리렌더링이 일어난다.

ref

https://velog.io/@mnz/React-useRef-개념부터-활용까지

HTML에서 DOM 요소에 id 같은 특정 값을 지정하고 자바스크립트에서 해당 DOM을 querySelector 등으로 조작할 수 있는데, 리액트에서 DOM을 조작하는 방법이 바로 ref를 사용하는 것이다.

const App = () => {
  const inputRef = useRef<HTMLInputElement>(null);
  const handleSubmit = (e: any) => {
    e.preventDefault();
    console.log(inputRef.current);

    if (inputRef.current) inputRef.current.value = '';
  };

  return (
    <div className="App">
      <form onSubmit={handleSubmit}>
        <input ref={inputRef} />
      </form>
    </div>
  );
};

특정 input에 포커스를 주거나, 스크롤 박스를 조작하거나, Canvas 요소에 그림을 그려야 하는 등 state로는 불가능한, DOM을 직접 건들이는 처리들을 해주어야 할 때 ref를 사용한다.

import React, { useRef } from 'react';

const App = () => {
  const localVarRef = useRef<number>(1);
  const handleButtonClick = () => {
    if (localVarRef.current) {
      localVarRef.current += 1;
      console.log(localVarRef.current);
    }
  };

  return (
    <div className="App">
      {localVarRef.current}
      <button onClick={handleButtonClick}>+1</button>
    </div>
  );
};

useRef는 .current 프로퍼티로 전달된 인자(initialValue)로 초기화된 변경 가능한 ref 객체를 반환하는데, 본질적으로 useRef는 .current 프로퍼티에 변경 가능한 값을 담고 있는 상자와도 같다. 그렇기에 DOM을 조작하는데 쓰이는 것이 아니라 리렌더링이 되면 안되는 컴포넌트의 변수로 사용하기도 한다.

타입스크립트에서의 useRef

https://driip.me/7126d5d5-1937-44a8-98ed-f9065a7c35b5

import { useRef } from 'react';
const Parents = () => {
  const ref = useRef<HTMLInputElement>(null);
  const handleChange = (e: any) => {
    console.log(e);
    console.log(ref.current);
  };
  return (
    <>
      <input type="text" ref={ref} onChange={handleChange} />
    </>
  );
};

타입스크립트 아니랄까봐 useRef를 선언할 때도 타입을 지정해주어야 하는데, 용도에 따라 사용되는 타입이 다른다.

자세한 건 위의 링크에 잘 정리되어 있으니 결론만 말하자면

로컬 변수 용도로 useRef를 사용하는 경우, const localVarRef = useRef<number>(0); 형태로 선언하고, DOM을 직접 조작하기 위해 프로퍼티로 useRef 객체를 사용할 경우, const inputRef = useRef<HTMLInputElement>(null);형태로 (초깃값 null) 선언해주면 된다.

profile
씨앗 개발자

0개의 댓글