Skip to content

SeonHyungJo/react-hook-sample

Repository files navigation

react-hook-sample

Contents

Basic Hooks

1. useState

const [state, setState] = useState(initialState);

첫번째 인자로 초기값 을 넣어준다. useState 는 우리가 많이 사용하던 state 초기화 작업과 동일하다고 생각하면 쉽다. 생성시 우리는 배열을 받아 올 수 있다.

모든 Hook은 2개의 인자를 배열로 받아온다.(드물게 아닌 것도 있다.)

배열의 첫번째는 우리가 넘겨주었던 초기화 값이 들어오고 이후에는 변경된 값이 넘어오게 된다. 두번째는 값를 변경하는 함수다.

re-render가 될 경우 useState에 의해 반환된 첫 번째 값은 항상 업데이트를 적용한 후 가장 최근 상태가 된다.


1.1 Functional updates

역시나 우리는 인자로 함수를 보낼수 있다. 그러나 기존에 우리가 사용하던 setState 메소드와는 달라서 자동으로 병합을 시켜주지 않는다.

즉 우리가 만들때 마다 필요에 따라 병합을 해줘야한다.

setState(prevState => {
  // Object.assign would also work
  return {...prevState, ...updatedValues}; // 간단하게 spread를 사용해서 새로 생성
});

1.2 Lazy initial state

initial state 에 역시 함수를 사용할 수 있다. 리액트 사이트에서 표현으로는 값 비싼 계산 이라면 함수를 사용해서 표현하라고 한다.

const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});

1.3 Bailing out of a state update

같은 값을 다시 업데이트를 하더라도 리액트에서는 자식을 re-render 하거나 effect 를 발생시키지 않는다.(Object.is를 사용해서 비교했다고 함_읽으면 유용함)


2. useEffect()

useEffect(() => {
  // Update the document title using the browser API
  document.title = `You clicked ${count} times`;
});

기본적으로 모든 렌더링이 완료된 후에 실행이 된다.

어떤 value 가 바뀌었을때 실행되도록 할 수 있다.(내가 바라보고 싶은 인자를 넣을 수 있다.)


2.1 Cleaning up an effect

화면을 떠나기 전에 정리를 할때 사용할 수 있다.(dispose()와 같은 느낌임)

아래의 예시를 보게 되면 return으로 만든 함수가 정리를 하는 함수이다.

useEffect(() => {
  const subscription = props.source.subscribe();
  return () => {
    // Clean up the subscription
    subscription.unsubscribe();
  };
});

메모리 부족을 방지하하기 위해 컴포넌트가 UI로 부터 제거되기 전에 실행한다.

만약에 구독을 취소하기전에 다른 effect가 실행이 되어 구독을 한다면? 이러한 일이 발생하는 것을 막고 싶다면?


2.2 Timing of effects

componentDidMountcomponentDidUpdate 과는 다르게 layoutpaint 가 끝나고 나서 useEffect가 실행이된다. 이렇게 되니 만든 공통의 부작용에 있어서 적합하다 라고 하는데, 화면에서 업데이트되는 것이 멈추는 현상이 일어나지 않으니까 그런 것이다.

예를 들어, 사용자가 볼 수있는 DOM 변이는 시각적인 불일치를 인식하지 못하도록 다음 페인트 전에 동기적으로 실행해야 한다.

useLayoutEffectuseEffect 동일한 기능이지만 실행되는 시점이 다른 Hook 이다. 쉽게 말함면 실행되는 시점이 다르다는 것인데 useEffect 는 아래에 진한 글씨와 같이 완전히 화면이 그려졌다 라는 것을 보장할 수 있지만 useLayoutEffect는 시점이 Dom의 변화는 있지만 repaint, reflow가 되지 않은 시점이라고 생각하면 될 것 같다.

useEffect는 브라우저가 paint할때 까지 연기를 하지만 새로운 렌더링전에 시작될 것이 보증한다.


2.3 Conditionally firing an effect

useEffect(
  () => {
    const subscription = props.source.subscribe();
    return () => {
      subscription.unsubscribe();
    };
  },
  [props.source],
);

useEffect 에는 2번째 인자가 존재한다. 여기에 들어가는 배열은 내가 바라볼 것 으로 여기서는 props.source 의 변경이 일어났을 경우에만 작동하게 된다.

만약에 인자에 아무것도 없다면 마운트에서 실행되고 마운트 해제에서는 정리한다.


3. useContext()

    const context = useContext(Context);

react@16.3부터 지원을 한 Context 를 사용할 수 있도록 지원하는 것이다. context object 를 받아서 현재 context 값을 반환한다.

provider가 업데이트되면 Hooks이 실행되어 최신의 값으로 당연히 동기화가 된다.


Additional Hooks

4. useReducer

우리가 리덕스에서 많이 보던 Reducer 와 비슷하다고 생각하면 된다. 그렇다고 한다.

    const initialState = {count: 0};
    
    // 우리가 Redux에서 만들던 Reducer 부분
    function reducer(state, action) {
      switch (action.type) {
        case 'increment':
          return {count: state.count + 1};
        case 'decrement':
          return {count: state.count - 1};
        default:
          throw new Error();
      }
    }
    
    function Counter({initialCount}) {
      // 초기화 값과 Reducer를 넣어준다.
      const [state, dispatch] = useReducer(reducer, initialState);

      return (
        <>
          Count: {state.count}
          <button onClick={() => dispatch({type: 'increment'})}>+</button>
          <button onClick={() => dispatch({type: 'decrement'})}>-</button>
        </>
      );
    }

4.1 Specifying the initial state

2번째 인자로 초기화 state 를 넘겨준다.(간단하게 초기화하는 방법)

    const [state, dispatch] = useReducer(
        reducer,
        {count: initialCount}
      );

4.2 Lazy initialization

만약 느슨하게 초기 state 를 만들고 싶다면 3번째 인자로 init function 을 넘기자

그러면 init(initialArg) 이런식으로 호출이 될 것이다.

    function init(initialCount) {
      return {count: initialCount};
    }
    
    function reducer(state, action) {
      switch (action.type) {
        case 'increment':
          return {count: state.count + 1};
        case 'decrement':
          return {count: state.count - 1};
        case 'reset':
          return init(action.payload);
        default:
          throw new Error();
      }
    }
    
    function Counter({initialCount}) {
      const [state, dispatch] = useReducer(reducer, initialCount, init);
      return (
        <>
          Count: {state.count}
          <button
            onClick={() => dispatch({type: 'reset', payload: initialCount})}>
    
            Reset
          </button>
          <button onClick={() => dispatch({type: 'increment'})}>+</button>
          <button onClick={() => dispatch({type: 'decrement'})}>-</button>
        </>
      );
    }

4.3 Bailing out of a dispatch

useState 와 동일하게 같은 값을 넘기게 되면 내부적으로 비교를 해서 자식 렌더링과 effect를 실행하는 것을 막는다.


5. useCallback

    const memoizedCallback = useCallback(
      () => {
        doSomething(a, b);
      },
      [a, b],
    );

Returns a memoized callback.

인라인 콜백과 입력 배열을 전달합니다. useCallback 은 입력 중 하나가 변경된 경우에만 변경되는 콜백의 memoized 버전을 반환합니다

a 또는 b 가 변경이 되었다면 함수 결과를 반환한다.

  • 동일한 표현
    useCallback(fn, inputs) //is equivalent to 
    useMemo(() => fn, inputs).

6. useMemo

    const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

Returns a memoized value.

렌더링되는 동안useMemo에 전달된 함수가 실행이 된다.

즉, 사용하는데 있어서 조심해야한다. 렌더링동안 하지않는 것을 하지 않아야한다. side effect를 위해 useEffect를 사용해야하는 것이다.

useEffect 와 동일하게 배열이 넘어가지 않을 경우 매순간 첫번째 매개변수를 타게 된다.

성능 최적화를 위해서 useMemo를 사용할 수 있을 것으로 생각이 된다.


7. useRef

    function TextInputWithFocusButton() {
      const inputEl = useRef(null);
      const onButtonClick = () => {
        // `current` points to the mounted text input element
        inputEl.current.focus();
      };

      return (
        <>
          <input ref={inputEl} type="text" />
          <button onClick={onButtonClick}>Focus the input</button>
        </>);
    }

8. useImperativeHandle

    useImperativeHandle(ref, createHandle, [inputs])

useImperativeHandleref 를 사용할 때 부모 구성 요소에 노출되는 인스턴스 값을 사용자 정의합니다.

    function FancyInput(props, ref) {
      const inputRef = useRef();

      useImperativeHandle(ref, () => ({
        focus: () => {
          inputRef.current.focus();
        }
      }));

      return <input ref={inputRef} ... />;
    }
    FancyInput = forwardRef(FancyInput);

9. useLayoutEffect

서명은 useEffect와 동일하지만 모든 DOM 변이 후에 동기적으로 시작 됩니다.

useLayoutEffect 내부에서 예약된 업데이트는 브라우저가 페인트할 수 있기 전에 동기적으로 플러시됩니다.

이것을 사용하여 DOM에서 레이아웃을 읽고 동기적으로 다시 렌더링합니다.

시각적인 업데이트를 막지 않도록 useEffect를 사용하기를 권장함


10. useDebugValue

useDebugValueReact DevTools에서 사용자 정의 후크 레이블을 표시하는 데 사용할 수 있습니다.

    function useFriendStatus(friendID) {
      const [isOnline, setIsOnline] = useState(null);
    
      // ...
    
      // Show a label in DevTools next to this Hook
      // e.g. "FriendStatus: Online"
      useDebugValue(isOnline ? 'Online' : 'Offline');
    
      return isOnline;
    }

10.1 Defer formatting debug values

    useDebugValue(date, date => date.toDateString());

Reference

-usehooks

About

This is place to study react-hook. Let's create custom hooks

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published