8장 Hooks


useEffect

리액트 컴포넌트가 렌더링될 때마다 특정 작업 을 수행하도록 설장할 수 있는 Hook
componentDidMount 와 componentDidupdate 합친 형태로 보면 됨.

useEffect(callback, 변경주시변수들의 배열);

두번째 인자로 어떤 값을 전달하느냐에 따라서 실행되는 조건이 달라짐. 기본적으로 첫 렌더링되고 난 직후에는 실행된다.

뒷정리하기

언마운트 되기 전이나 업데이트되기 직전에 특정 함수 호출하고 싶으면 useEffect 콜백함수에서 cleanup 함수 반환해 주면 된다.
업데이트 되기 직전의 값을 보여줌.

useEffect(() => {
    console.log("useEffect");
    console.log(name);
    return () => {
      console.log("cleanup");
      console.log(name);
    };
  }, [name]);

useReducer

액션 값을 전달받아 새로운 상태를 반환하는 함수

액션 : 업데이트를 위해 필요한 정보를 담은 값
새로운 상태 만들 때는 불변성을 지켜줘야 한다.
첫번째 파라미터로 리듀서 함수, 두번째 파라미터로 해당 리듀서의 기본값 넣어줌.

function reducer(state, action) {
	return { ... }; // 불변성을 지키면서 업데이트한 새로운 상태를 반환합니다.
}
// 액션 값 예시
{
  type: 'INCREMENT',
  // 다른 값들이 필요하다면 추가로 들어감
}
function reducer(state, action) {
  switch (action.type) {
    case "INCREMENT":
      return { value: state.value + 1 };
    case "DECREMENT":
      return { value: state.value - 1 };
    default:
      return state;
  }
}
const Counter2 = () => {
  const [state, dispatch] = useReducer(reducer, { value: 0 });

  return (
    <div>
      <p>
        현재 카운터 값은 <b>{state.value}</b>입니다.
      </p>
      <button onClick={() => dispatch({ type: "INCREMENT" })}>+1</button>
      <button onClick={() => dispatch({ type: "DECREMENT" })}>-1</button>
    </div>
  );
};

return 값으로 state 값과 dispatch 함수 받아온다. state는 현재 가리키고 있는 상태
dispatch는 액션을 발생시키는 함수이다. dispatch(action) 같은 형태로 쓰이며, 해당 액션 값으로 리듀서 함수가 호출되는 구조.

사용은 17장 리덕스 사용하여 리액트에서 상태 관리하기

인풋 상태 관리하기

useState는 input여러개 일때 개별적으로 관리했는데, useReducer 이용해서 동적으로 관리하기

function reducer(state, action) {
  return {
    ...state,
    [action.name]: action.value,
  };
}

const Info = () => {
  const [state, dispatch] = useReducer(reducer, { name: "", nickName: "" });
  const onChange = (e) => {
    dispatch(e.target);
  };
  ...
};

useMemo

렌더링하는 과정에서 특정 값이 바뀌었을 때만 연산을 실행, 원하는 값이 바뀌지 않았으면 이전에 연산했던 결과를 다시 사용.
두번재 인자로 특정값을 배열 형태로 지정
리렌더링 할 때 마다, 각 요소들의 { } 안에 값들이 다시 평가되는 것 같음. 만약 { } 안에 함수가 있다면 재호출되어서 계산을 반복하게 됨.
⇒ 해당 값을 가지고 있다가, 바뀌지 않았으면 저장된 기존 값을 반환해서 2번 계산하기 않게 한다.

const getAverage = (numbers) => {
  console.log("평균값 계산 중..");
  if (numbers.length === 0) return 0;
  const sum = numbers.reduce((a, b) => a + b);
  return sum / numbers.length;
};

const Average = () => {
  const [list, setList] = useState([]);
  const [number, setNumber] = useState("");

  const onChange = (e) => {
    setNumber(e.target.value);
  };
  const onInsert = (e) => {
    const nextList = list.concat(parseInt(number));
    setList(nextList);
    setNumber("");
  };
  const avg = useMemo(() => getAverage(list), [list]);
  return (
    <div>
      <input value={number} onChange={onChange} />
      <button onClick={onInsert}>등록</button>
      <ul>
        {list.map((value, index) => (
          <li key={index}>{value}</li>
        ))}
      </ul>
      <div>
        <b>평균값:</b>
        {avg}
      </div>
    </div>
  );
};

useCallback

UseMemo의 함수 버전으로 생각하면 된다.
컴포넌트가 리렌더링될 때 마다 함수를 새로만들지 않고 기존 함수 사용
첫번째 인자로 함수, 두번째 인자로 배열
위에 코드에서 OnChange 와 OnInsert 같은 함수들이 리렌더링 될 때마다 새로 만들어진다. 이를 최적화할 수 있다.

	const onChange = useCallback((e) => {
    setNumber(e.target.value);
  }, []);
  const onInsert = useCallback(
    (e) => {
      const nextList = list.concat(parseInt(number));
      setList(nextList);
      setNumber("");
    },
    [number, list]
  );
  const

useRef

함수 컴포넌트에서 ref 사용하기.
useRef 를 통해 만든 객체 안의 current 값이 실제 요소를 가르킴.

const Average = () => {
	...
  const inputEl = useRef(null);
	...
  const onInsert = useCallback(
    (e) => {
      const nextList = list.concat(parseInt(number));
      setList(nextList);
      setNumber("");
      **inputEl.current.focus();**
    },
    [number, list]
  );
	...
  return (
    <div>
      <input value={number} onChange={onChange} ref={**inputEl**} />
	...

로컬변수 사용하기

class MyComponent extends Component {
  id = 1
  setId = (n) => {
    this.id = n;
  }
  printId = () => {
    console.log(this.id);
  }
  render() {
    return (
      <div>
        MyComponent
      </div>
    );
  }
}

class 형 컴포넌트에서 클래스 필드로 정의한 로컬 변수를 함수 컴포넌트에서는 useRef이용해서 로컬변수로 사용할 수 있다.

const RefSample = () => {
  const id = useRef(1);
  const setId = (n) => {
    id.current = n;
  }
  const printId = () => {
    console.log(id.current);
  }
  return (
    <div>
      refsample
    </div>
  );
};

ref안의 값이 바뀌어도 컴포넌트가 리렌더링 되지 않는다.
→ 렌더링과 관련되지 않은 값을 관리할 때만 사용해라

커스텀 Hooks 만들기

import { useReducer } from 'react';
 
function reducer(state, action) {
  return {
    ...state,
    [action.name]: action.value
  };
}
 
export default function useInputs(initialForm) {
  const [state, dispatch] = useReducer(reducer, initialForm);
  const onChange = e => {
    dispatch(e.target);
  };
  return [state, onChange];
}
import React from 'react';
import useInputs from './useInputs';
 
const Info = () => {
  const [state, onChange] = useInputs({
    name: '',
    nickname: ''
  });
  const { name, nickname } = state;
 
  return (
    <div>
      <div>
        <input name="name" value={name} onChange={onChange} />
        <input name="nickname" value={nickname} onChange={onChange} />
      </div>
      <div>
        <div>
          <b>이름:</b> {name}
        </div>
        <div>
          <b>닉네임: </b>
          {nickname}
        </div>
      </div>
    </div>
  );
};
 
export default Info;