React 1. Udemy(2)

khxxjxx·2021년 7월 28일
0

Udemy

목록 보기
3/9
post-custom-banner

1. Udemy(2)

Custom Hooks

  • Custom Hook을 만들때에는 보통 use 라는 키워드로 시작하고 그안에 함수를 작성한다
  • 반복되는 state와 관련된 로직을 쉽게 재사용할 수 있다
  • Custom Hook을 사용한 컴포넌트의 모든 state와 effect는 완벽하게 분리되어있다(Custom Hook을 사용할 때마다 그 안에있는 모든 state와 effects는 완전히 독립적)
  • 일반함수와 달리 사용자 정의 후크는 다른 사용자 정의 후크를 호함한 다른 React 후크를 호출할 수 있다

Redux

  • useContext와 Redux를 이용하면 전역상태 관리를 할 수 있다
  • 모든 JavaScript 프로젝트에서 Redux를 사용할 수 있다
  • React에서 Redux를 연결하기 위해선 react-redux를 추가로 설치해야 한다
    • npm install || yarn add redux react-redux
  • 크게 전역 상태를 보관하는 하나의 중앙 데이터 저장소 Store, 상태 저장소에 접근을 위한 reducer, 리듀서에 행동을 지시하는 action, 저장소에 보관된 상태를 가져오는 subscription으로 나뉘어져 있다
    • 리액트에서는 Store를 주로 index.js에 정의한다
  • 전역 상태 관리 외에도 로컬스토리지 상태저장, 버그리포트 첨부 기능 등의 기능들을 사용할 수 있어 상태 및 변경되는 데이터를 관리하는데 도움이 된다

Store

  • 새로운 변수에 redux라이브러리에서 제공하는 createStore함수를 담고 인자로 reducer함수를 받는다
    • createStore : 저장소 생성
    • const store = createStore(리듀서 함수)
  • react-redux라이브러리에서 제공하는 Provider를 이용하여 Store 사용을 원하는 컴포넌트를 감싸주고 store를 전달한다
    • <Provider store={store}>

Reducer

  • reducer함수 안에다 state초기값과, state 데이터의 수정방법(action : action 으로부터 객체를 받아 type 검사를 통해 데이터를 수정)을 미리 정의해놓고 사용해야한다
  • 단, 상태가 추가되는 것이 아닌 덮어씌워지게 되므로 전체 상태를 "복사"하여 상태를 갱신한 후에 반환해야 한다 !절대로 기존상태를 직접 조작하면 안된다!
  • 정의된 수정방법을 실행하려면 dispatch() 함수를 이용한다
  • dispatch할 때 데이터를 실어보낼 수도 있다
  • reducer가 여러개일땐 redux 라이브러리에서 제공하는 combineReducers를 이용할 수 있다
  • createStore(combineReducers({ reducer, reducer2 }))

Subscription

react-redux 라이브러리에서 제공하는 Hooks(use...)과 HOC(connect)가 있다

  • useStore

    • 컴포넌트 안에서 store를 사용할 수 있다
    • 이미 변경이 완료된 값을 가져오는 것이기에 상태변화를 감지 못해서 useEffect가 인식을 못한다
    • const store = useStore()
    • store.getState()
  • useSelector

    • useSelector(selector : Function, deps : any[])
    • const counter = useSelector(state => state.counter)
    • 해당 redux의 상태가 바뀌면 그 state를 사용하는 컴포넌트가 다시 렌더링된다
    • 성능 최적화를 위해 store에 저장된 state를 쪼개서 필요한 것만 가져올 수 있다
    • deps 배열은 어떤 값이 바뀌었을 때 selector 를 재정의 할 지 설정해준다
    • deps 값을 생략 하면 매번 렌더링 될 때마다 selector 함수도 새로 정의된다
  • useDispatch

    • redux store에 설정된 action에 대한 dispatch를 연결하는 hook
    • const dispatch = useDispatch()
  • connect

    • HOC로 class형 컴포넌트에서도 사용이 가능하다
    • 사용을 원하는 컴포넌트 하단에 connect함수를 만들고 export해준다
    • export default connect(connect함수명)(Component명)
    • connect함수를 사용하는 경우 해당 컨테이너 컴포넌트의 부모 컴포넌트가 리렌더링될 때 해당 컴포넌트의 props가 바뀌지 않았다면 리렌더링이 자동으로 방지되지만 hook은 그렇지 않기 때문에 성능 최적화를 따로 해줘야한다
// 예시

// store/index.js 정의
import { createStore } from 'redux';

const initialState = { counter: 0, showCounter: true };

const counterReducer = (state = initialState, action) => {
  if (action.type === 'increment') {
    return {
      counter: state.counter + 1,
      showCounter: state.showCounter,
      // 데이터가 추가되는것이 아닌 덮어씌어지기때문에 값의 변화가 없더라도 명시해야한다
    };
  }
  if (action.type === 'increase') {
    return {
      counter: state.counter + action.amount, // 데이터를 받아올수도 있다
      showCounter: state.showCounter,
    };
  }
  if (action.type === 'decrement') {
    return {
      counter: state.counter - 1,
      showCounter: state.showCounter,
    };
  }
  if (action.type === 'toggle') {
    return {
      counter: state.counter,
      showCounter: !state.showCounter,
    };
  }

  return state;
};

const store = createStore(counterReducer);

export default store;


// src/index.js 정의
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';

import App from './App';
import store from './store/index';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);


// 최종전달 컴포넌트
import { useSelector, useDispatch } from 'react-redux';

const Counter = () => {
  const dispatch = useDispatch();
  const counter = useSelector(state => state.counter);
  const show = useSelector(state => state.showCounter);

  const incrementHandler = () => {
    dispatch({ type: 'increment' });
  };

  const increaseHandler = () => {
    dispatch({ type: 'increase', amount: 5 });
  };
  
  const decrementHandler = () => {
    dispatch({ type: 'decrement' });
  };
  
  const toggleCounterHandler = () => {
    dispatch({ type: 'toggle' });
  };

  return (
    <main>
      <h1>Redux Counter</h1>
      {show && <div className={classes.value}>{counter}</div>}
      <div>
        <button onClick={incrementHandler}>Increment</button>
        <button onClick={increaseHandler}>Increment by 5</button>
        <button onClick={decrementHandler}>Decrement</button>
      </div>
      <button onClick={toggleCounterHandler}>Toggle Counter</button>
    </main>
  );
};


// 최종전달 컴포넌트 class형으로 변환
import { Component } from 'react';
import { connect } from 'react-redux';

class Counter extends Component {
  incrementHandler() {
    this.props.increment();
  }

  decrementHandler() {
    this.props.decrement();
  }

  render() {
    return (
      <main>
        <h1>Redux Counter</h1>
        <div>{this.props.counter}</div>
        <div>
          <button onClick={this.incrementHandler.bind(this)}>Increment</button>
          <button onClick={this.decrementHandler.bind(this)}>Decrement</button>
        </div>
      </main>
    );
  }
}

const mapStateToProps = state => {
  return {
    counter: state.counter,
  };
};

const mapDispatchToProps = dispatch => {
  return {
    increment: () => dispatch({ type: 'increment' }),
    decrement: () => dispatch({ type: 'decrement' }),
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(Counter);
profile
코린이
post-custom-banner

0개의 댓글