gincheong / Memo

개발 관련 내용들을 메모하기 위한 용도로 만든 빈 레포지토리입니다.
0 stars 0 forks source link

React와 Redux #26

Open gincheong opened 3 years ago

gincheong commented 3 years ago
npm install redux
npm install react-redux
gincheong commented 3 years ago

Action

// ./action/myAction.js
export const ACTION_TYPE = {
  addNumber: '@myApp/ADD_NUMBER'
};

export const addNumber = input => ({
  type: ACTION_TYPE.addNumber,
  payload: input
});
gincheong commented 3 years ago

Reducer

// ./reducer/myReducer.js
import { ACTION_TYPE } from '../action';

const initialState = {
  value: 0
};

export function myReducer (state = initialState, action) {
  switch (action.type) {
    case ACTION_TYPE.addNumber:
      return { ...state, value: state.value + action.payload };
      // action의 실행 결과를 가지고 새로운 state를 반환함
    default:
      return state;
  }
}
// ./reducer/index.js
import { combineReducers } from 'redux';

import { myReducer } from './myReducer';

export const rootReducer = combineReducers({
  myReducer
});
// 최종적으로 만든 리듀서들을 하나로 합쳐야 함
gincheong commented 3 years ago

Store

// ./index.js

...
import { createStore, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux';

import { rootReducer } from './reducer';
...

const store = createStore(
  rootReducer,
  compose(applyMiddleware()) // 필요하다면 여기에 middleware 추가
);

ReactDOM.render(
  <Provider store={store}>
      ...
  </Provider>,
  ...
);

...
gincheong commented 3 years ago

Components

// ./components/myComponent.js
...
import { connect } from 'react-redux';
import { addNumber } from '../action';
...
class myComponent extends Component {
  ...
}

// Redux Store의 state를 조회하여, 어떤 데이터를 컴포넌트의 props로 넣어줄 지 정의함
// 컴포넌트에서 this.props.value로 리듀서에 저장된 값에 접근 가능
const mapStateToProps = state => ({
  value: state.myReducer.value
});

// Action을 dispatch하는 함수를 만들어 props에 넣어줌
// 컴포넌트에서 this.props.onIncrease 로 action 실행 가능
const mapDispatchToProps = dispatch => ({
  onIncrease: (input) => dispatch(addNumber(input))
});

export default connect(mapStateToProps, mapDispatchToProps)(myComponent);
gincheong commented 3 years ago

[추가] Redux-Thunk

Action에서 API 통신 등을 하기 위해 비동기 처리가 필요해지면, async, await 키워드를 넣어서 코드를 작성하면 된다고 생각할 수 있는데, 그러면 오류를 발생시키면서 작동하지 않는다.

Error: Actions must be plain objects. Use custom middleware for async actions.

말 그대로, 커스텀 미들웨어를 사용해야 액션에서 비동기 처리를 할 수 있어지는데, 대표적으로 비동기 처리를 위해 redux-thunk를 많이 사용한다.

npm install redux-thunk

원래 action은 순수 plain object를 반환하던 형태였지만, 이제 비동기 처리를 위해서 dispatch 함수를 인자로 받고 그에 대한 작업 결과를 Promise 객체로 반환하는 함수를 리턴 해야 한다.

export const addNumber = input => ({
  type: ACTION_TYPE.addNumber,
  payload: input
});

에서

export const addNumber = input => async dispatch => {
  await fetch(URL)
    .then(async res => {
      dispatch({
        type: ACTION_TYPE.addNumber,
        payload: await res.json()
      })
    }
  )
};

이렇게

그리고 추가로 middleware를 설정하려면, 위의 Store에서 applyMiddlewarethunk를 추가하면 된다.

import thunk from 'redux-thunk';
...

const store = createStore(
  rootReducer,
  compose(applyMiddleware(thunk)) // 필요하다면 여기에 middleware 추가
);