20230210 [react] - react hook

lionloopy·2023년 2월 10일
0

리액트

목록 보기
7/18

useState

:가장 기본적인 hook이다. (카운터나 todolist에 활용!)
함수형 컴포넌트 내에서 가변적인 상태를 가지게 한다.

const [state, setState] = useState(초기값)

state:현재값, setState:state를 변경할 수 있는 set함수(최신상태를 나타냄)
useState:초기값인 배열( [ { } ] )로 이루어져 있다.

기존 업데이트

function App() {
  const [number, setNumber] = useState(0);

  return (
    <div>
      <div>{number}</div>
      <button
        onClick={() => {
          setNumber(number + 1);
        }}
      >
        up
      </button>
    </div>
  );
}

:배치 업데이트가 된다. 만약 setNumber(number+1)구문이 여러개로 늘어난다면, 모두 똑같은 내용을 담기 때문에 1개로 치고 한 번만 실행하여 숫자는 +1밖에 되지 않는다.

함수형 업데이트

function App() {
  const [number, setNumber] = useState(0);

  return (
    <div>
      <div>{number}</div>
      <button
        onClick={() => {
          setNumber((currentNumber) => {
            return currentNumber + 1;
          });
        }}
      >
        up
      </button>
    </div>
  );
}

:순차 업데이트가 된다. setNumber안에 함수를 넣어주어 함수형으로 여러개 적는다면, 함수가 실행될 때마다 1번씩 실행하여 숫자는 +3이 된다.

useEffect

:렌더링 될 때마다 특정한 작업을 수행해야 할 때 설정하는 hook이다.
가리키는 것 없이 렌더링 될 때마다 함수로서 실행되며, 조건을 걸려면 의존성 배열로 건다.

어떤 컴포넌트가 화면에서 딱! 보여졌을 때 어떠한 것을 실행하고 싶거나,
어떤 컴포넌트가 화면에서 사라졌을 때 실행하고 싶을 때 활용한다.

기본코드

function App() {
  useEffect(() => {
    alert("hello");
  });
  return <div>App</div>;
}

:렌더링 될 때마다 alert가 뜨게 된다.

작동 방식

function App() {
  const [value, setValue] = useState("");
  useEffect(() => {
    console.log("hello");
  });
  return (
    <div>
      <input
        type="text"
        value={value}
        onChange={(event) => {
          setValue(event.target.value);
        }}
      />
    </div>
  );
}

이렇게 코드를 짜면, input값에 값을 입력할 때 마다 콘솔에 hello가 찍힌다.
1.value, setValue로 useState를 만들고, useEffect로 콘솔 hello문구를 만든다.
2.input에 value값을 넣어주어 input에 값을 입력하면
3.value, 즉 state가 변경된다.
4.state가 변경되었기 때문에 App컴포넌트가 리렌더링 된다.
5.리렌더링 되기 때문에 useEffect가 실행되어
6.console에 hello가 찍히게 된다.

의존성 배열
:이 배열에 값을 넣으면 그 값이 바뀔 때만 useEffect를 실행한다.
위처럼 계속 불필요하게 렌더링 되면 좋지 않으니까,
[] 배열 안 조건 값에 의존하여 렌더링 한다.

function App() {
  const [value, setValue] = useState("");
  useEffect(() => {
    console.log("hello");
    return () => {
    console.log('bye') }
    }, []);
  return (
    <div>
      <input
        type="text"
        value={value}
        onChange={(event) => {
          setValue(event.target.value);
        }}
      />
    </div>
  );
}

useEffect함수 뒤에 배열을 추가해주고, 그 배열 안에 조건을 넣어주면 된다.
배열이 비어있을 때는 어떠한 값이 변하던지 간에 화면이 처음 로딩될 때만 동작한다.

clean up
:useEffect안에 return으로 함수값을 넣어주면 컴포넌트가 죽을 때 동작을 한다.

useRef

1.저장공간으로 사용된다.(state와 비슷)
:state는 리렌더링이 꼭 필요한 값을 다룰 때 쓰면 되고,
ref는 리렌더링을 발생시키지 않는 값을 저장할 때 사용한다.
2.DOM요소에 접근할 수 있도록 하는 hook이다.
:렌더링이 되자마자 특정 input이 포커싱 되어야 한다면 사용한다.

설정된 ref값은 컴포넌트가 계속해서 렌더링이 되어도 이 컴포넌트가 죽기 전까지 값을 유지한다.

사용 용도
1.저장공간으로 사용된다.
=>state와 비슷하게 사용된다. 하지만 state는 변화가 일어나면 다시 렌더링이 일어나고 내부 변수들은 초기화가 된다.
=>ref에 저장한 값은 렌더링을 일으키지 않는다. 렌더링으로 인해 내부 변수들이 초기화 되는 것을 막을 수 있다.
=>state는 리렌더링이 꼭 필요한 값을 다룰 때 쓰면 되고,
ref는 리렌더링을 발생시키지 않는 값을 저장할 때 사용한다.
2.DOM요소에 접근하는 용도로 사용된다.
=>렌더링이 되자마자 특정 input이 포커싱 되어야 한다면 사용한다.

기본코드

function App() {
  const ref = useRef("초기값");
  ref.current = '변경값'
  
  return <div>App</div>;
}

:변수로 선언하고 useRef(초기값)을 담는다. 변수명에 .current를 찍으면 변경값을 알 수 있다.

1.저장공간으로 쓰일 때(state와의 차이)

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

  const plusState = () => {
    setCount(count + 1);
  };

  const plustRef = () => {
    countRef.current++;
  };

  return (
    <div>
      <div>
        state영역입니다. {count}
        <br />
        <button onClick={plusState}>state증가</button>
      </div>

      <div>
        ref영역입니다. {countRef.current} <br />
        <button onClick={plustRef}>ref증가</button>
      </div>
    </div>
  );
}

한 개의 버튼은 state로, 한 개의 버튼은 ref로 설정했을 때,
이렇게 되면 state증가 버튼을 누르면 0에서 숫자가 1씩 계속 올라가며 렌더링이 되지만,
ref증가 버튼을 누르면 값은 올라가서 콘솔에 찍히지만, 렌더링이 되지 않으므로 숫자에 변화가 나타나지 않는다.

2.DOM에 접근(포커싱)

function App() {
  const idRef = useRef("");

  useEffect(() => {
    idRef.current.focus();
  });
  return (
    <div>
      <div>
        아이디 : <input type="text" />
      </div>
      <div>
        비밀번호 : <input type="password" ref={idRef} />
      </div>
    </div>
  );
}

렌더링이 될 때마다 값을 변경하기 위해 useEffect함수를 먼저 만든다.
계속해서 렌더링이 되어도 값을 유지하며 DOM에 접근하기 위해 useRef를 사용한다.
const로 idRef를 만들고 초기값은 비워둔다. 그리고 이 ref={변수명}을 input에 넣어준다. 여기서 idRef는 input그 자체를 가리키게 된다. 그리고 useEffect안에idRef.current.focus()를 넣어준다. 이 구문은 렌더링할 때 마다 포커싱(커서)가 해당 input에 가도록 한다.

아이디를 10자리 입력하면 패스워드로 넘어가도록 해보자

function App() {
  const idRef = useRef("");
  const pwRef = useRef("");

  const [id, setId] = useState("");

  useEffect(() => {
    idRef.current.focus();
  }, []);

  useEffect(() => {
    if (id.length >= 10) {
      pwRef.current.focus();
    }
  }, [id]);
  
  return (
    <div>
      <div>
        아이디 :{" "}
        <input
          value={id}
          onChange={(event) => {
            setId(event.target.value);
          }}
          type="text"
          ref={idRef}
        />
      </div>
      <div>
        비밀번호 : <input type="password" ref={pwRef} />
      </div>
    </div>
  );
}

아이디와 패스워드 모두 순서대로 커서가 가야 하니까 useEffect를 두 개 따로 만든다. 아이디가 10자리 넘으면 패스워드로 넘어가야 하므로, input값을 onChange로 받아온다. useEffect에 if로 조건을 넣어 id의 length가 10보다 같거나 크면 pwRef로 포커스가 가도록 한다. 이 때 id는 useState로 맏늘고, useEffect의 의존성 배열 [ ] 칸에 id라는 조건값을 넣어준다. 그러면 id값이 바뀔 때만 useEffect를 실행한다.

useContext

:pros를 통해 부모가 자식에게 데이터를 전달할 수 있지만, 이게 무한대로 길어지게 되면 prop drilling이 되며 어디서 온 지 출처를 알기가 어려워진다. 그래서 context API가 등장했다.

필수개념

  • createContent: context 생성
  • consumer: context 변화 감지
  • provider: context 하위 컴포넌트에게 전달

useContext를 사용하지 않을 때
App.js

import React from "react";
import GrandFather from "./GrandFather";

function App() {
  return <GrandFather />;
}

export default App;

GrandFather.js

import React from "react";
import Father from "./Father";

function GrandFather() {
  const houseName = "스파르타";
  const pocketMoney = 10000;
  return <Father houseName={houseName} pocketMoney={pocketMoney} />;
}

export default GrandFather;

Father.js

import React from "react";
import Child from "./Child";

function Father({ houseName, pocketMoney }) {
  return <Child houseName={houseName} pocketMoney={pocketMoney} />;
}

export default Father;

Child.js

import React from "react";

function Child({ houseName, pocketMoney }) {
  return (
    <div>
      나는 이 집안의 막내이다. 할아버지가 우리 집 이름은 {houseName}이라고
      하셨고, 나에게 {pocketMoney}를 주셨다.
    </div>
  );
}

export default Child;

useContext를 사용했을 때
FamilyContext.js

import { createContext } from "react";

export const FamilyContext = createContext(null);

App.js

import React from "react";
import GrandFather from "./GrandFather";

function App() {
  return <GrandFather />;
}

export default App;

GrandFather.js

import React from "react";
import { FamilyContext } from "./FamilyContext";
import Father from "./Father";

function GrandFather() {
  const houseName = "스파르타";
  const pocketMoney = 10000;
  return (
    <FamilyContext.Provider
      value={{
        houseName: houseName,
        pocketMoney: pocketMoney,
      }}
    >
      <Father />;
    </FamilyContext.Provider>
  );
}

export default GrandFather;

Father.js

import React from "react";
import Child from "./Child";

function Father() {
  return <Child />;
}

export default Father;

Child.js

import React, { useContext } from "react";
import { FamilyContext } from "./FamilyContext";

function Child() {
  const data = useContext(FamilyContext);
  return (
    <div>
      나는 이 집안의 막내이다. 할아버지가 우리 집 이름은 {data.houseName}이라고
      하셨고, 나에게 {data.pocketMoney}를 주셨다.
    </div>
  );
}

export default Child;

1.FamilyContext를 만들어주고, 거기서 createContext를 한 다음 초기값으로 null을 넣어준다.
2.App.js는 그대로 GrandFather 자식 컴포넌트를 품고 있다.
3.GrandFather에서 선언한 변수와 값은 그대로 가지고 있고, FamilyContext로 전체 틀을 감싼다. 데이터를 자식에게 전달하기 위해 provider을 붙인다. 그리고 value값으로 전달할 값들을 담는다.
4.그럼 Father로 왔을 때 props를 전달받을 필요가 없어진다. Child만 품고있게 된다. FamilyContext는 가장 상위에 있는 데이터로(전역) 접근이 모두 가능하기 때문이다.
5.Child에서 똑같이 props로 받을 필요가 없어지고, data라는 변수명을 선언하고 useContext를 만든다. 그 값에는 FamilyContext를 담는다. 그리고 return문 안에는 { }자바스크립트 구문을 이용하되, data.value값을 적어주면 된다.
=> useContext를 사용하지 않을 땐, 부모가 자식에게만 값을 전해줄 수 있으므로 계속해서 넘기고, 넘기고 이어서 넘겨야 했다.
=> 하지만 useContext를 이용하면 전역 데이터를 만들 수 있고 거기에 모두 접근이 가능하기에 굳이 이어서 넘겨줄 필요가 없어진다.

profile
Developer ʕ ·ᴥ·ʔ ʕ·ᴥ· ʔ

0개의 댓글