* useState
* const [name, setName] = useState(‘’);
* useEffect
* componentDidMount + componentDidUpdate
* useEffect은 함수를 컴포넌트가 화면에 맨 처음 렌더링될때만 실행
* 두번째 파라미터로 비어있는 배열을 넣어주면 업데이트될때 수행되지 않는다.
* 특정 값이 변경될 때만 호출하고 싶은 경우는 두번째 파라미터로 넘긴다.
* 특정값을 변경하는 코드를 함께 넣고, 두번째 파라미터에서 특정값이 변경될때 호출하면 무한으로 useEffet가 호출됨
* 업데이트 되기 직전이나, 언마운트 되기 전에 작업을 수행하고 싶으면 return () => { .. cleanup } 함수를 반환한다.
* useReducer
* 더 다양한 컴포넌트 상황에 따라 다양한 상태를 다른 값으로 업데이트 해주고 싶을때 사용
* 현재 상태, 업데이트를 위해 필요한 정보를 담는 action값을 전달받아 새로운 상태를 반환하는 함수
* 새로운 상태를 만들 때는 반드시 불변성을 유지?
* 액션 객체는 반드시 type을 지니고 있을 필요가 없다. (문자열, 숫자 상관없음)
* 컴포넌트 업데이트 로직을 컴포넌트 바깥으로 빼낼 수 있다.
* event.target 을 사용하면 action.name을 state로 사용을 할 수 있다.
* reducer에 return { ...state , [action.name]: action.value }}


function reducer(state, action) {
switch (action.type) {
case "INCREMENT":
return {value: state.value + 1}

...
}
}

const [state, dispatch] = useReducer(reducer, {value:0}); // 두번째 파라미터에는 해당 리듀서의 기본값
// state는 현재 가리키고 있는 상태, dispatch는 액션을 발생시키는 함수
onClick => dispatch({type: 'INCREMENT'})}

* useMemo
* 렌더링하는 과정에서 특정 값이 바뀌었을 때만 연산을 실행하고, 원하는 값이 바뀌지 않았다면 이전에 연산했던 결과를 다시 사용
* 값을 재사용(숫자, 문자열, 객체)
* const avg = useMemo(() => getAverage(list), [list]);
* useCallback
* 함수를 재사용
* useMemo와 유사하게 주로 렌더링 성능을 최적화해야 하는 상황에서 사용
* 컴포넌트가 리렌더링 될때마다 함수들이 새로생기기 때문에 useCallback을 이용
* 함수에 useCallBack을 추가해서 컴포넌트가 처음 렌더링될때만 함수 생성 or 특정 값이 변경되었을때만 함수생성(=useEffect와 유사함)
* useRef
* 컴포넌트에서 ref를 쉽게 사용할 수 있도록 해준다.
* 예) 컴포넌트에서 특정 버튼을 눌렀을때 input에 포커스를 가게 하기
* inputEl = useRef(null); <input ref={inputEl} /> inputEl.current.focus()
* 컴포넌트 로컬 변수로 사용 (로컬변수는 렌더링과 상관없이 바뀔수 있는 값) 클래스에서 사용했던 로컬변수를 함수형으로 컴포넌트를 선언하기 위해서
* id = useRef(1), setId = (n) => { id.current = n; } printId = () => {console.log(id.current}
* CustomHooks
* https://github.com/rehooks/awesome-react-hooks
* https://nikgraf.github.io/react-hooks

  • Context API는 리액트 프로젝트에서 전역적으로 사용할 데이터가 있을 때 유용한 기능
  • 사용 예
    • 로그인 정보
    • 애프리케이션 환경 설정
    • 테마
    • redux, react-router, styled-components 의 라이브러리
  • 컴포넌트 간에 데이터를 props로 저달하기 때문에 여기저기서 필요한 데이터가 있을 때는 주로 최상위 컴포넌트인 App의 state에 넣어서 관리
  • 하지만 props으로 아주 ~ 먼 컴포넌트에 전달하려면 여러개의 컴포넌트를 지나가야 하니... 유지 보수성이 낮아질 가능성이 있음
  • 위 문제점을 해결하기 위해서 redux나 MobX 같은 상태 관리 라이브러리를 사용하면 전역 상태 관리를 더 편하게 처리
  • 리액트 v16.3 업데이트 이후에는 Context API가 많이 개선되었기 때문에 별도의 라이브러리 사용할 필요 없음
import { createContext } from 'react';

const ColorContext = createContext({ color: 'black' });

export default ColorContext; 
import ColorBox from './components/ColorBox';
<ColorBox></ColorBox>

Provider 이용하기

  • Provider를 사용하면 Context의 값을 변경할 수 있다.
  • 아래와 같이 컴포넌트 ColorBoxColorContext.Provider를 통해 color 값을 변경할 수 있다.
  • Provider를 사용할때는 value 값을 명시해줘야 제대로 작동
      <ColorContext.Provider value={{ color: 'red' }}>
        <div>
          <ColorBox></ColorBox>
        </div>
      </ColorContext.Provider>

동적으로 사용하기

  • context의 value에는 상태값, 함수를 전달할 수 있다.
  • 아래와 같이 state, actions을 따로 정의한 ColorContext를 정의
  • ColorProvider 를 정의
import React, { createContext, useState } from 'react';

// 객체에서 state, actions을 분리해서 정의하면 나중에 사용하기가 편함
const ColorContext = createContext({
  state: { color: 'black', subcolor: 'red' },
  actions: {
    setColor: () => {},
    setSubcolor: () => {}
  }
});

const ColorProvider = ({ children }) => {
  const [color, setColor] = useState('black');
  const [subcolor, setSubcolor] = useState('red');

  const value = {
    state: { color, subcolor },
    actions: { setColor, setSubcolor }
  };

  return (
    <ColorContext.Provider value={value}>{children}</ColorContext.Provider>
  );
};

const { Consumer: ColorConsumer } = ColorContext;
export { ColorProvider, ColorConsumer };
export default ColorContext;
import React from 'react';
import { ColorConsumer } from '../contexts/color';

const ColorBox = () => {
  return (
    <ColorConsumer>
      {(
        { state } // 객체 비구조화 할당 문법
      ) => (
        <>
          <div
            style={{
              width: '64px',
              height: '64px',
              background: state.color
            }}
          />
          <div
            style={{
              width: '32px',
              height: '32px',
              background: state.subcolor
            }}
          />
        </>
      )}
    </ColorConsumer>
  );
};

export default ColorBox;

사용하는 쪽에서는 아래와 같이

      <ColorProvider>
        <div>
          <ColorBox></ColorBox>
        </div>
      </ColorProvider>

색상 선택하는 컴포넌트 생성

  • Context에 들어있는 actions에 넣어준 함수를 호출하는 컴포넌트 생성
# SelectColor.js
import React from 'react';
import { ColorConsumer } from '../contexts/color';

const colors = [
  'red',
  'oragne',
  'yellow',
  'green',
  'blue',
  'indigo',
  'violoet'
];

const SelectColors = () => {
  return (
    <div>
      <h2>색상을 선택하세요.</h2>
      <ColorConsumer>
        {({ actions }) => (
          <div style={{ display: 'flex' }}>
            {colors.map(color => (
              <div
                key={color}
                style={{
                  background: color,
                  width: '24px',
                  height: '24px',
                  cursor: 'pointer'
                }}
                onClick={() => actions.setColor(color)}
                onContextMenu={e => {
                  e.preventDefault(); // 기존 오른쪽 메뉴가 뜨는 것을 방지
                  actions.setSubcolor(color);
                }}
              />
            ))}
          </div>
        )}
      </ColorConsumer>
      <hr />
    </div>
  );
};

export default SelectColors;
  • App.js에서는 SelectColors의 컴포넌트를 추가
      <ColorProvider>
        <div>
          <SelectColors></SelectColors>
          <ColorBox></ColorBox>
        </div>
      </ColorProvider>

useContext Hook 사용

  • 리액트 내장되어 있는 Hooks 중에서 useContext를 사용해보자
  • useContext Hook은 함수형 컴포넌트에서만 사용할 수 있다.
  • ColorBox를 아래와 같이 수정
import React, { useContext } from 'react';
import { ColorContext } from '../contexts/color';

const ColorBox = () => {
  const { state } = useContext(ColorContext);
  return (
    <>
      <div
        style={{
          width: '64px',
          height: '64px',
          background: state.color
        }}
      />
      <div
        style={{
          width: '32px',
          height: '32px',
          background: state.subcolor
        }}
      />
    </>
  );
};

export default ColorBox;

static contextType

  • 클래스형 컴포넌트에서 Context를 좀 더 쉽게 사용하기 위해서 static contextType을 정의
  • 기존에 SelectColors의 컴포넌트를 클래스형으로 변경
  • static contextType을 정의하면 클래스 메소드에서도 Context에 있는 함수 호출이 가능
  • 단점이라면 한 클래스에서 하나의 context만 사용할 수 있음
  • useContext를 사용하는게 더 권장됨
  • 아래가 기존
import React from 'react';
import { ColorConsumer } from '../contexts/color';

const colors = [
  'red',
  'oragne',
  'yellow',
  'green',
  'blue',
  'indigo',
  'violoet'
];

const SelectColors = () => {
  return (
    <div>
      <h2>색상을 선택하세요.</h2>
      <ColorConsumer>
        {({ actions }) => (
          <div style={{ display: 'flex' }}>
            {colors.map(color => (
              <div
                key={color}
                style={{
                  background: color,
                  width: '24px',
                  height: '24px',
                  cursor: 'pointer'
                }}
                onClick={() => actions.setColor(color)}
                onContextMenu={e => {
                  e.preventDefault(); // 기존 오른쪽 메뉴가 뜨는 것을 방지
                  actions.setSubcolor(color);
                }}
              />
            ))}
          </div>
        )}
      </ColorConsumer>
      <hr />
    </div>
  );
};

export default SelectColors;
  • 이후가 클래스 컴포넌트로 변경 후
import React, { Component } from 'react';
import ColorContext from '../contexts/color';

const colors = [
  'red',
  'oragne',
  'yellow',
  'green',
  'blue',
  'indigo',
  'violoet'
];

class SelectColors extends Component {
  static contextType = ColorContext;

  handleSetColor = color => {
    this.context.actions.setColor(color);
  };

  handleSetSubcolor = subcolor => {
    this.context.actions.setSubcolor(subcolor);
  };

  render() {
    return (
      <div>
        <h2>색상을 선택하세요.</h2>
        <div style={{ display: 'flex' }}>
          {colors.map(color => (
            <div
              key={color}
              style={{
                background: color,
                width: '24px',
                height: '24px',
                cursor: 'pointer'
              }}
              onClick={() => this.handleSetColor(color)}
              onContextMenu={e => {
                e.preventDefault(); // 기존 오른쪽 메뉴가 뜨는 것을 방지
                this.handleSetSubcolor(color);
              }}
            />
          ))}
        </div>
        <hr />
      </div>
    );
  }
}

export default SelectColors; 

+ Recent posts