react hooks(useState, useEffect, uesRef, useContext, useMemo, useCallback, customHook)

katanazero·2020년 8월 18일
5

react

목록 보기
5/15
post-thumbnail

😍react hooks ?

  • 함수형 컴포넌트에서 기능을 추가할때 사용하는 함수
  • hook 을 사용하여 상태값 관리 등 기능을 구현(16.8 버전에 추가)
  • useState, useEffect, uesRef, useContext, useMemo, useCallback, customHook 에 대한 내용을 다룰거다.
  • 예제코드 : https://github.com/katanazero86/react-hooks-example

useState

  • 컴포넌트에 상태값(state)을 추가할 때 사용하는 hook
  • useState 훅이 반환하는 배열의 첫번째 요소는 상태값이며, 두번째 요소는 상태값 변경 함수
  • 상태값 변경 함수가 호출되면 컴포넌트는 re-rendering 되며, re-rendering 과정에서 자식 컴포넌트도 같이 렌더링 된다.
  • 참조타입을 상태값으로 사용하는 경우, 얕은비교/깊은비교에 주의해야 한다.(얕은비교로 처리한다)
  • 리액트는 가능하다면, 상태값 변경을 배치(batch)로 처리(🙏주의! 리액트 외부에서 관리되는 이벤트 처리 함수의 경우에는 상태값 변경이 배치(batch)로 처리되지 않음!)
import React, {useState} from 'react';

function App() {

  const [count, setCount] = useState(0);

  return (
    <div>
        react hooks
        <div>
            <p>
                useState
            </p>
            <p>
                {count}<br/>
                <button onClick={()=> setCount(count+1)}>값 증가</button>
            </p>
        </div>
    </div>
  );
}

export default App;
function incrementCount() {
        // count 상태값을 2번 증가시키려고 한다.
        setCount(count+1);
        setCount(count+1);
}
  • 이런형식으로 상태값 변경 함수를 연속으로 호출하면, 의도와는 다르게 1번만 증가를 한다.(상태값 변경 함수가 비동기로 동작하기 때문이다. -> 동기로 처리하는 경우 성능 이슈가 생길 수 있다.)
function incrementCount2() {
        // incrementCount() 의 문제를 아래와 같이 해결이 가능
        setCount(prevState => prevState + 1);
        setCount(prevState => prevState + 1);
}
  • 상태값 변경 함수의 인자를 함수로 입력이 가능하며, 해당 함수는 이전 상태값을 매개변수로 받는다. 실제로 이러한 방식으로 처리하면 상태값은 2만큼 증가하여 UI에 정상적으로 반영이된다.

useEffect

  • 컴포넌트의 부수효과를 처리할 때 사용하는 hook
  • 부수효과는 API 호출, 이벤트 처리등이 있음
import React, {useEffect} from 'react';

export default function EffectComponent() {


    // useEffect
    useEffect(()=> {
        console.log(`useEffect in EffectComponent`);
    });

    // useEffect
    useEffect(()=> {
        console.log(`useEffect in EffectComponent(first render)`);
    },[]);

    return (
        <div>
            EffectComponent
        </div>
    )

}
  • useEffect 훅은 부수효과 함수와 의존성 배열을 인자로 받는다.
  • 의존성 배열에 값을 입력하면, 배열의 값이 변경되는 경우에만 실행이 된다.
  • 빈배열을 주는경우, 첫 렌더링에만 실행하겠다는 의미
  • 의존성 배열을 주지 않는 경우, 컴포넌트가 렌더링 할 때마다 실행이 된다.
 // useEffect
    useEffect(()=> {
        const onResize = () => console.log(`resize event`);
        window.addEventListener('resize', onResize);
        return () => {
            window.removeEventListener('resize', onResize);
        }
    },[]);
  • useEffect 훅을 이용해서 이벤트 처리 함수를 등록하고 해제하는 코드
  • useEffect 훅 첫번째 인자인 부수효과 함수는 함수를 반환할 수 있으며, 컴포넌트가 사라지기 직전에 마지막으로 호출된다.

customHook

  • 리액트는 제공하는 훅을 이용해서 커스텀(custom) 훅을 만들 수 있음
  • 훅을 직접 만들어서 사용하면, 쉽게 로직을 재사용할 수 있음
  • 커스텀 훅의 이름은 use 로 시작하는게 좋음
import {useEffect, useState} from 'react';

export default function useMounted() {
    const [mounted, setMounted] = useState(false);

    useEffect(() => {
        console.log('-- useEffect in useMounted --');
        setMounted(true);
    }, []);

    return mounted;
}
  • useState / useEffect 훅을 이용해서 커스텀 훅을 제작
  • 내부구현은 숨기면서 사용 편이성을 높일수 있다.
  • 🙏주의! 컴포넌트에서 훅을 호출하는 순서는 항상 같아야 한다.
  • 🙏주의! 훅은 함수형 컴포넌트 또는 커스텀 훅 안에서만 호출되어야 한다.
    -> 훅은 리액트 함수형 컴포넌트를 위한 기능이므로 당연히 함수형 컴포넌트에서만 사용이 가능하며, 내부적으로 훅에서 사용된 순서를 배열형태로 저장하여 순서를 기억하고 있기 때문에 순서를 지켜줘야한다.
function Profile() {
  const [age, setAge] = useState(0); // 2
  const [name, setName] = useState('홍길동'); // 3
  
  useEffect(()=>{
    setAge(10); // 1 age 상태값을 변경한다.
  },[]);
  
  //...
  
}
  • 1 age 상태값을 10으로 변경한다. 리액트는 첫번째 훅의 상태값을 10으로 변경
  • 2 가 만약에 조건문에 의해 실행되지 않는다면, 3 의 name 값이 23이 되므로 문제가 된다.
  • 😤이러한 조건문에 의한 훅이 실행되는 코드를 짜지는 않을테지만, 주의해야함!

useContext

  • context API

    보통 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달할 때 props(속성값) 를 사용하는데 가까운 depth 에 있는 몇 개의 하위 컴포넌트로 전달할 때는 props 로 충분하다.

    문제는 많은 수의 하위 컴포넌트로 또는 depth 가 많아질수록 props 로 내려주는 코드를 반복적으로 작성해야하는 문제가 발생한다. (이는 가독성 + 유지보수성도 좋지않다)

    이런 경우에 context API 를 사용하면 컴포넌트의 중첩 구조가 복잡한 상황에서도 비교적 쉽게 데이터를 전달할 수 있다. (물론, redux / mobx 같은 상태관리라이브러리를 사용하여 해결해도 된다)

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

export default function App() {
  return (
    <div className="App">
      <Child msg='hello Child Component'/>
    </div>
  );
}


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

export default function Child(props) {
    return (
        <div>
            <p>{props.msg}</p>
            <Child2 msg={props.msg}/>
        </div>
    )
}

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

export default function Child2(props) {
    return (
        <div>
            <p>{props.msg}</p>
            <Child3 msg={props.msg}/>
        </div>
    )
}

import React from "react";

export default function Child3(props) {
    return (
        <div>
            <p>{props.msg}</p>
        </div>
    )
}
  • App 컴포넌트에서 Child3 컴포넌트까지 msg 라는 속성값을 전달해주고 있다. 이게 컴포넌트들이 더 중첩이 되고 많아진다면 매우 복잡해질거다.
  • context API를 사용하면 App 컴포넌트에서 Child3 컴포넌트로 중간에 Child, Child2 컴포넌트가 개입하지 않고도 속성값을 전달할 수 있다.
import React from 'react';
import Child from "./components/Child";


// context API
export const MyContext = React.createContext(''); // createContext() 호출하여 context 객체 생성
// {Provider, Consumer}
// 상위 컴포넌트에서 Provider 컴포넌트를 이용해서 데이터를 전달
// 하위 컴포넌트에서 Consumer 컴포넌트를 이용해서 데이터를 사용

export default function App() {
    return (
        <div className="App">
            <MyContext.Provider value="hello Child3 Component">
                <Child msg='hello Child Component'/>
            </MyContext.Provider>
        </div>
    );
}



import React from "react";

// context API
import {MyContext} from '../App.js';

export default function Child3(props) {
    return (
        <MyContext.Consumer>
            {msg => <p>{msg}</p>}
        </MyContext.Consumer>
    )
}
  • React.createContext(defaultValue) => {Provider, Consumer}
  • 상위 컴포넌트에서 Provider 컴포넌트를 이용해서 데이터를 전달
  • 하위 컴포넌트에서 Consumer 컴포넌트를 이용해서 데이터를 사용
  • Consumer 컴포넌트는 데이터를 찾기 위해, 상위로 올라가면서 가장 가까운 Provider 컴포넌트를 찾는다. 만약, Provider 컴포넌트를 찾지 못한다면 기본값을 사용.
  • Provider 컴포넌트의 속성값이 변경되면 하위의 모든 Consumer 컴포넌트는 다시 랜더링 된다. (🙏 중요! 중간에 위치한 컴포넌트의 랜더링 여부와 상관없이 Consumer 컴포넌트는 다시 랜더링 된다.)
import React, {useState} from 'react';
import Child from "./components/Child";


// context API
export const MyContext = React.createContext(''); // createContext() 호출하여 context 객체 생성
// {Provider, Consumer}
// 상위 컴포넌트에서 Provider 컴포넌트를 이용해서 데이터를 전달
// 하위 컴포넌트에서 Consumer 컴포넌트를 이용해서 데이터를 사용

export default function App() {

    const [msg, setMsg] = useState('hello Child3 Component');

    return (
        <div className="App">
            <button onClick={()=> setMsg('안녕? 3번째 자식 컴포넌트!')}>메시지 변경</button>
            <MyContext.Provider value={msg}>
                <Child msg='hello Child Component'/>
            </MyContext.Provider>
        </div>
    );
}

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


export default function Child(props) {
    return (
        <div>
            <p>{props.msg}</p>
            <Child2 msg={props.msg}/>
        </div>
    )
}

import React, {useState} from "react";
import Child3 from "./Child3";


const Child3Memo = React.memo(()=>{
   return (<Child3/>)
});


export default function Child2(props) {

    const [name, setName] = useState('zzzz');

    return (
        <div>
            <p>{props.msg} / {name}</p>
            <button onClick={() => setName('test')}>이름 변경</button>
            <Child3Memo/>
        </div>
    )
}

import React from "react";

// context API
import {MyContext} from '../App.js';

export default function Child3() {

    console.log(`Child3 Component rendered..`);

    return (
        <MyContext.Consumer>
            {msg => <p>{msg}</p>}
        </MyContext.Consumer>
    )
}
  • Child3 컴포넌트를 React.memo(컴포넌트의 props 가 바뀌지 않았다면, 리렌더링을 방지하여 컴포넌트의 리렌더링 성능 최적화를 해줄 수 있는 함수) 를 사용해서 다시 랜더링 되지 않도록 수정함
  • Provider 가 바뀌면 console 메세지는 출력이 안되지만, 값이 변경되는건 확인이 가능하다.
  • Child2 컴포넌트에서 name 상태값을 변경하면 다시 랜더링이 될텐데, React.memo 로 Child3 컴포넌트를 다시 랜더링하지 않도록 하였기 때문에, console 메세지는 출력이 안된다.
  • 😤주의! 컨텍스트 데이터를 객체로 사용할 때 주의하지 않으면 불필요한 랜더링이 발생할 수 있다. (컨텍스트 데이터는 변하지 않았는데, 해당 컴포넌트가 랜더링이 될때마다 새로운 객체가 생성이 되어 하위의 Consumer 컴포넌트도 다시 랜더링 된다)

이제 Consumer 컴포넌트 없이 context API 를 사용하자!(useContext)

import React, {useState} from 'react';
import Child from "./components/Child";
import Child4 from "./components/Child4";


// context API
export const MyContext = React.createContext(''); // createContext() 호출하여 context 객체 생성
// {Provider, Consumer}
// 상위 컴포넌트에서 Provider 컴포넌트를 이용해서 데이터를 전달
// 하위 컴포넌트에서 Consumer 컴포넌트를 이용해서 데이터를 사용

export const MyContext2 = React.createContext('');

const Child4Memo = React.memo(() => {
    return (
        <Child4/>
    )
});

export default function App() {

    const [msg, setMsg] = useState('hello Child3 Component');

    return (
        <div className="App">
            <button onClick={() => setMsg('안녕? 3번째 자식 컴포넌트!')}>메시지 변경</button>
            <MyContext.Provider value={{msg, setMsg}}>
                <Child msg='hello Child Component'/>
                <MyContext2.Provider value={msg}>
                    <Child4Memo/>
                </MyContext2.Provider>
            </MyContext.Provider>
        </div>
    )
}


import React, {useContext} from "react";

import {MyContext2} from '../App';

export default function Child4() {

    const myContext = useContext(MyContext2); // useContext 훅을 사용

    console.log('Child4 Component rendered..');

    return (
        <div>
            <p style={{padding : '4px', border : '1px dotted gray'}}>
                Child4 Component : {myContext}
            </p>
        </div>
    )
}
  • Consumer 컴포넌트를 사용하면 JSX 부분이 복잡해지는 단점이 있지만, useContext 훅은 사용하기 간편하다.
  • Consumer 컴포넌트와는 다르게, useContext 훅을 사용한 컴포넌트는 Provider 값이 변경되면 해당 컴포넌트가 다시 랜더링이 일어난다. (useContext 훅을 사용하는 경우 해당 컴포넌트는 context 에 의존적인 컴포넌트가 되어버리기 때문에 context 데이터가 변경되면 컴포넌트가 re-rendering 되는게 당연하다.)

useRef

  • 직접 DOM 요소에 접근해야 할 때가 있는데, useRef 훅을 사용하여 DOM 요소에 직접 접근이 가능
  • useRef 훅이 반환하는 ref 객체를 이용해서 자식 요소에 접근이 가능
import React, {useEffect, useState, useRef} from "react";

function TextInput({inputRef}) {
    return (
        <input type='text' ref={inputRef}/>
    )
}

export default function RefComponent() {

    const [inputText, setInputText] = useState('');

    const inputRef = useRef();
    const inputRef2 = useRef();

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

    return (
        <div>
            <input type='text' ref={inputRef} onInput={(e) => setInputText(e.target.value)}/>
            <p>{inputText}</p>
            <TextInput inputRef={inputRef2}/>
            <button onClick={() => inputRef2.current.focus()}>포커스 이동</button>
        </div>
    )
}
  • 접근하고자 하는 자식 요소의 ref 속성값에 ref 객체를 입력
  • 해당 DOM 요소 또는 컴포넌트가 생성되면 ref 객체로 접근 가능(ref 객체의 current 속성을 이용)
  • ref 객체를 내려받아서 사용도 가능하다. (가독성이 떨어져서 그렇게 권장하지는 않음)
  • 😤주의! 컴포넌트가 생성된 이후라도 ref 객체의 current 속성이 없을 수 있기 때문에 주의해야함.(조건부 렌더링을 하는 경우에는 current 속성을 검사하는 로직이 필요)
import React, {useRef} from "react";

const TextInput2 = React.forwardRef((props, ref) => {
    return (
        <input type='text' ref={ref}/>
    )
});

export default function RefComponent() {

    const inputRef3 = useRef();

    return (
        <div>
            <TextInput2 ref={inputRef3}/>
            <button onClick={() => inputRef3.current.focus()}>포커스 이동</button>
        </div>
    )
}
  • React.forwardRef 함수를 사용하면, 부모 컴포넌트에서 넘어온 ref 속성값을 직접 처리할 수 있다.
  • ref 예약어를 그대로 사용 가능
import React, {useRef, useState} from "react";

const INIT_TEXT = 'HI';

export default function RefComponent2() {

    const [text, setText] = useState(INIT_TEXT);
    const [showText, setShowText] = useState(true);

    return (
        <div>
            {showText &&
            <input type='text'
                   value={text}
                   onInput={(e) => setText(e.target.value)}
                   ref={(ref) => ref && setText(INIT_TEXT)}
            />
            }
            <button onClick={() => setShowText(!showText)}>보이기/가리기</button>
        </div>

    )
}
  • ref 속성값에 함수를 사용 가능
  • ref 속성 값으로 입력한 함수는 해당 요소가 제거되거나 생성될 때마다 호출된다. 요소가 생성될 때는 해당 요소를 참조하는 변수가 넘어오고, 삭제될 때는 null 값이 넘어온다.
  • 위 예제코드에서는 입력이 정상동작하지 않는다 -> 상태값이 변경되면서 re-rendering 된다. -> re-rendering 되면서, ref 속성에 새로운 함수를 넣어버리기 때문에 INIT_TEXT 인 HI값으로 계속 초기화가 이루어짐. -> useCallback 훅으로 수정 가능
import React, {useState, useCallback, useRef, useEffect} from "react";


const INIT_TEXT = 'HI';
const INIT_TEXT2 = 'HI2';

export default function RefComponent2() {

    const [text, setText] = useState(INIT_TEXT);
    const [text2, setText2] = useState(INIT_TEXT2);
    const [showText, setShowText] = useState(true);
    const prevText2Ref = useRef();

    useEffect(() => {
        // text2 값이 변경될때마다..
        prevText2Ref.current = text2;
    },[text2]);

    const setInitText = useCallback((ref) => ref && setText2(INIT_TEXT2), []); // 한번 생성된 함수를 계속 재사용 한다.
    const prevText2Value = prevText2Ref.current;

    return (
        <div>
            {showText && <div>
                <input type='text'
                       value={text}
                       onInput={(e) => setText(e.target.value)}
                       ref={(ref) => ref && setText(INIT_TEXT)}
                />

                <input type='text'
                       value={text2}
                       onChange={(e) => setText2(e.target.value)}
                       ref={setInitText}
                />
            </div>
            }
            <button onClick={() => setShowText(!showText)}>보이기/가리기</button>
            <p>
                {`text2 이전값 : ${prevText2Value}`}
            </p>
        </div>

    )
}
  • useRef 훅을 사용하여, 렌더링과 무관한 값을 저장할 수도 있다.
  • 비동기로 동작을 하기때문에, 이전 상태값을 꺼내서 prevText2Value 에 저장

memoization hook(useMemo, useCallback)

  • 메모이제이션(memoization)은 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술
  • 이전 값을 기억해서 성능을 최적화하는 기술
  • useMemo : useMemo 훅은 계산량이 많은 함수의 반환값을 재활용하는 용도로 사용
import React, {useState, useMemo} from 'react';

export default function App() {

  const [number, setNumber] = useState(1);

  const memoNumber = useMemo(()=> number * 100, [number]);

  return (
    <div>
      <div style={{boarder : '1px dotted skyblue'}}>
        <h2>useMemo</h2>
        <p>
          useMemo 훅은 계산량이 많은 함수의 반환값을 재활용하는 용도로 사용
        </p>
        <p>
          {`memoNumber : ${memoNumber}`}
        </p>
      </div>
    </div>
  );
}
  • 첫번째 인자로 함수를 받는다. useMemo 훅은 이 함수가 반환하는 값을 기억

  • 두번째 인자로 의존성 배열을 받는다. 의존성 배열이 변경되지 않으면 이전에 반환된 값을 재사용(vue.js 에서 computed 를 사용하는 느낌)

  • useCallback : useCallback 훅은 리액트의 렌더링 성능을 위해 제공되는 훅이며, 컴포넌트가 렌더링될 때마다 새로운 함수를 생성해서 자식 컴포넌트의 속성값으로 입력하는 경우가 많다. 리액트팀에서는 브라우저에서 함수 생성이 성능에 미치는 영향이 적다고 주장한다. 그보다는 속성값이 매번 변경되기 때문에 React.memo 함수를 사용해도 불필요한 렌더링이 발생한다는 문제점이 있다.

import React, {useState, useMemo} from 'react';
import UserInfo from "./components/UserInfo";


const UserInfoMemo = React.memo((props) => {
    return (
        <UserInfo name={props.name} age={props.age} updateUserInfo={props.updateUserInfo}/>
    )
});

export default function App() {

    const [number, setNumber] = useState(1);
    const [userInfo, setUserInfo] = useState({name: 'test', age: 20});

    const memoNumber = useMemo(() => number * 100, [number]);

    const updateUser = (targetName, targetAge) => {
        setUserInfo({
            name: targetName,
            age: targetAge
        });

    };

    return (
        <div>
            <div style={{boarder: '1px dotted skyblue', padding: '4px'}}>
                <h2>useMemo</h2>
                <p>
                    useMemo 훅은 계산량이 많은 함수의 반환값을 재활용하는 용도로 사용
                </p>
                <p>
                    {`memoNumber : ${memoNumber}`}
                </p>
                <button onClick={() => setNumber(number + 1)}>값 변경</button>
            </div>
            <div style={{boarder: '1px dotted skyblue', padding: '4px'}}>
                <h2>useCallback</h2>
                <p>
                    useCallback 훅은 리액트의 렌더링 성능을 위해 제공되는 훅
                </p>
                <p>
                    값 변경을 누르면, App 컴포넌트가 상태값이 변경되었기 때문에 re-rendering 된다.<br/>
                    UserInfo 컴포넌트를 React.memo 를 사용했는데, console 에 계속 렌더링 메세지가 출력이 된다. <br/>
                    이유는 App 컴포넌트가 re-rendering 되면서, updateUser 함수가 새로 생성이 되고 이를 속성값 변경이기 때문에 불필요한 렌더링이 발생된다.<br/>
                </p>
                    <UserInfoMemo name={userInfo.name}
                                  age={userInfo.age}
                                  updateUserInfo={updateUser}/>
            </div>
        </div>
    );
}

// UserInfo component
import React from "react";

export default function UserInfo({name, age, updateUserInfo}) {

    console.log(`UserInfo Component rendered..`);

    return (
        <div>
            <p>
                {name} / {age}
            </p>
            <button onClick={() => updateUserInfo('변경이름', 30)}>고객정보변경</button>
        </div>
    )
}
  • React.memo 함수를 이용하여 UserInfo 컴포넌트를 UserInfoMemo 컴포넌트로 생성
  • App 상태값이 변경될때마다 UserInfo 컴포넌트도 같이 불필요한 렌더링 발생(속성값이 변하지 않았는데 발생) 이유는, updateUser() 함수를 매번 새로 생성해서 속성값으로 내려주기 때문 -> 새로운 함수가 늘 입력되는 거다.
  • userInfo 속성값이 변경되지 않는다면 항상 같아야 한다 -> useCallback 훅을 사용하여 개선이 가능하다.
import React, {useState, useMemo, useCallback} from 'react';
import UserInfo from "./components/UserInfo";
import UserInfo2 from "./components/UserInfo2";


const UserInfoMemo = React.memo((props) => {
    return (
        <UserInfo name={props.name} age={props.age} updateUserInfo={props.updateUserInfo}/>
    )
});

const UserInfoMemo2 = React.memo((props) => {
    return (
        <UserInfo2 name={props.name} age={props.age} updateUserInfo={props.updateUserInfo}/>
    )
});

export default function App() {

    const [number, setNumber] = useState(1);
    const [userInfo, setUserInfo] = useState({name: 'test', age: 20});

    const memoNumber = useMemo(() => number * 100, [number]);

    const updateUser = (targetName, targetAge) => {
        setUserInfo({
            name: targetName,
            age: targetAge
        });
    };

    const updateUserCallback = useCallback((targetName, targetAge) => updateUser(targetName, targetAge), [userInfo.name, userInfo.age]);


    return (
        <div>
            <div style={{boarder: '1px dotted skyblue', padding: '4px'}}>
                <h2>useMemo</h2>
                <p>
                    useMemo 훅은 계산량이 많은 함수의 반환값을 재활용하는 용도로 사용
                </p>
                <p>
                    {`memoNumber : ${memoNumber}`}
                </p>
                <button onClick={() => setNumber(number + 1)}>값 변경</button>
            </div>
            <div style={{boarder: '1px dotted skyblue', padding: '4px'}}>
                <h2>useCallback</h2>
                <p>
                    useCallback 훅은 리액트의 렌더링 성능을 위해 제공되는 훅
                </p>
                <p>
                    값 변경을 누르면, App 컴포넌트가 상태값이 변경되었기 때문에 re-rendering 된다.<br/>
                    UserInfo 컴포넌트를 React.memo 를 사용했는데, console 에 계속 렌더링 메세지가 출력이 된다. <br/>
                    이유는 App 컴포넌트가 re-rendering 되면서, updateUser 함수가 새로 생성이 되고 이를 속성값 변경이기 때문에 불필요한 렌더링이 발생된다.<br/>
                </p>
                <UserInfoMemo name={userInfo.name}
                              age={userInfo.age}
                              updateUserInfo={updateUser}/>
                <p>
                    useCallback() 훅을 이용하여 개선한 컴포넌트
                </p>
                <UserInfoMemo2 name={userInfo.name}
                               age={userInfo.age}
                               updateUserInfo={updateUserCallback}/>
            </div>
        </div>
    );
}


// UserInfo2 component
import React from "react";

export default function UserInfo2({name, age, updateUserInfo}) {

    console.log(`UserInfo2 Component rendered..`);

    return (
        <div>
            <p>
                {name} / {age}
            </p>
            <button onClick={() => updateUserInfo('변경이름22', 50)}>고객정보변경2</button>
        </div>
    )
}
  • userInfo2 컴포넌트는 App 컴포넌트 상태값이 변경되어서 re-rendering 이 되어도 속성값이 변하지 않았기때문에 랜더링이 일어나지 않는다.
  • useCallback 훅은 첫번째 인자로 함수를 받고, 두번째 인자로 의존성 배열을 받는다. -> 의존성 배열이 변경되지 않으면 이전에 생성한 함수를 재사용

정리

  • useState 훅은 함수형 컴포넌트에서 상태값 관리를 위해 사용 / 상태값 변경 함수는 비동기로 동작 / 참조타입을 상태값으로 관리하는 경우 깊은비교, 얕은비교 주의 / 상태값 변경 시, 컴포넌트는 re-rendering(자식 컴포넌트 포함)

  • useEffect 훅은 함수형 컴포넌트에서 부수효과를 관리하기 위해 사용 / 의존성 배열에 따라 부수효과 함수 실행여부를 결정

  • useRef 훅은 실제 DOM 요소에 접근할때 사용하며, ref 객체를 통해 접근 / 조건부 렌더링을 사용하는 경우에 ref.current 속성이 없을수도 있기때문에 검사하는 로직이 필요 / useRef 훅을 사용하여 렌더링과 무관한 값을 저장가능 / ref 속성에 함수 지정 가능

  • useContext 훅은 Consumer 컴포넌트를 사용하지 않고 context API 로 부터 전달되는 데이터를 쉽게 사용 가능 / useContext 훅을 사용하는 경우 해당 컴포넌트는 context 에 의존적인 컴포넌트가 되어버리기 때문에 context 데이터가 변경되면 컴포넌트가 re-rendering

  • useCallback 훅은 컴포넌트가 렌더링될 때마다 새로운 함수를 생성하는 부분을 최적화 해줌 / useCallback 훅은 첫번째 인자로 함수를 받고, 두번째 인자로 의존성 배열을 받는다. -> 의존성 배열이 변경되지 않으면 이전에 생성한 함수를 재사용

  • useMemo 훅은 계산량이 많은 함수의 반환값을 재활용하는 용도로 사용 / useMemo 훅은 첫번째 인자로 받은 함수에서 처리한 반환값을 기억하며, 두번째 인자인 의존성 배열이 변경되지 않으면 이전에 반환된 값을 사용

profile
developer life started : 2016.06.07 ~ 흔한 Front-end 개발자

0개의 댓글