3주차 - 리덕스를 사용해서 값을 불러오고 수정 해보자
본문 바로가기
항해 중/3주차 리액트 기초반

3주차 - 리덕스를 사용해서 값을 불러오고 수정 해보자

by 은돌1113 2021. 11. 18.

1. 리덕스 써보기

 

1) 덕스(ducks) 구조

- 보통 리덕스를 사용할 때는, 모양새대로 action, actionCreator, reducer를 분리해서 작성합니다.

(액션은 액션끼리, 액션생성함수는 액션생성함수끼리, 리듀서는 리듀서끼리 작성합니다.)

- 덕스 구조는 모양새로 묶는 대신 기능으로 묶어 작성합니다.

(버킷리스트를 예로 들자면, 버킷리스트의 action, actionCreator, reducer를 한 파일에 넣는 거예요 -> 모듈이라고 한다.)

 

-> 우리는 덕스 구조로 리덕스 모듈을 만들어볼거예요!

(리덕스 모듈 -> 액션 타입, 액션 생성 함수, 리듀서가 모두 들어있는 자바스크립트 파일을 말한다.)

https://velog.io/@zayong/%EB%A6%AC%EB%8D%95%EC%8A%A4-%EB%AA%A8%EB%93%88-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0

 

리덕스 모듈 다시 이해하기

리덕스 모듈이란 다음 항목들이 모두 들어있는 자바스크립트 파일을 의미한다액션타입액션 생성함수리듀서리듀서와 액션관련 코드를 하나의 파일로 몰아서 개발하는 방식을 Ducks패턴이라고

velog.io

 

[외울 필요 없어요!]

덕스 구조를 잘 설명해 주는 사이트가 있습니다.

헷갈리실 때 들어가서 읽어보시면 되고, 모듈을 새로 만들 때 복사해서 쓰셔도 좋습니다.

// widgets.js

// Actions
const LOAD   = 'my-app/widgets/LOAD';
const CREATE = 'my-app/widgets/CREATE';
const UPDATE = 'my-app/widgets/UPDATE';
const REMOVE = 'my-app/widgets/REMOVE';

// Reducer
export default function reducer(state = {}, action = {}) {
  switch (action.type) {
    // do reducer stuff
    default: return state;
  }
}

// Action Creators
export function loadWidgets() {
  return { type: LOAD };
}

export function createWidget(widget) {
  return { type: CREATE, widget };
}

export function updateWidget(widget) {
  return { type: UPDATE, widget };
}

export function removeWidget(widget) {
  return { type: REMOVE, widget };
}

// side effects, only as applicable
// e.g. thunks, epics, etc
export function getWidget () {
  return dispatch => get('/widget').then(widget => dispatch(updateWidget(widget)))
}

https://github.com/erikras/ducks-modular-redux

 

GitHub - erikras/ducks-modular-redux: A proposal for bundling reducers, action types and actions when using Redux

A proposal for bundling reducers, action types and actions when using Redux - GitHub - erikras/ducks-modular-redux: A proposal for bundling reducers, action types and actions when using Redux

github.com

 

+ 미들웨어

: 외부에서 데이터를 받아오는 경우(서버로 부터 데이터를 받아오는 경우)에는 비동기이기 때문에 시간이 걸려서 바로 리듀서에서 수정을 해서 스토어에 넘겨 줄 수 없기 때문에 미들웨어를 사용한다.

// side effects, only as applicable
// e.g. thunks, epics, etc
export function getWidget () {
  return dispatch => get('/widget').then(widget => dispatch(updateWidget(widget)))
}

 

2) 첫번째 모듈 만들기

 

- 리덕스 모듈이란?

: 액션 타입, 액션 생성 함수, 리듀서가 모두 들어있는 자바스크립트 파일을 말한다.

 

(1) 폴더를 생성한다.

: src 폴더 아래에 redux라는 폴더를 만들고, 그 안에 modules라는 폴더를 만듭니다.

modules 아래에 bucket.js라는 파일을 만들고 리덕스 모듈 예제를 붙여 넣어 주세요.

아래 과정을 하나하나 밟으며 버킷리스트 항목을 리덕스에서 관리하도록 고쳐봅시다!

 

(2) Action

: 액션의 타입을 정해주는 곳

// Actions
// 액션의 타입을 정해주는 곳
const CREATE = 'bucket/CREATE'; // 추가하기 기능
// 프로젝트명/모듈명(리듀서명)/액션명

 

(3) InitalStore

: 초기 상태값을 만들어줄거예요! 그러니까, 기본 값이죠.

const initialState = { // 초기값 설정
    list : ["영화관 가기", "매일 책읽기", "수영 배우기"]
}

 

(3) Action Creator

: 액션 생성 함수를 만듭니다.

-> 액션 타입에 대한 액션 객체를 만드는 곳

// Action Creators (액션 생성 함수)
// 액션 타입에 대한 액션 객체를 만드는 곳
export function createBucket(bucket){ // CREATE Action에 대한 액션 객체
    return {
        type : CREATE,
        bucket
        // bucket : bucket -> bucket
        // key, value가 똑같으면 생략 가능(자바스크립만)
    };
}

 

(4) Reducer

: 리듀서를 작성합니다.

-> load할 땐, 가지고 있던 기본값을 그대로 뿌려주면 되겠죠?

create할 땐, 새로 받아온 값을 가지고 있던 값에 더해서 리턴해주면 될거예요!

(우리는 action으로 넘어오는 bucket이 text값인 걸 알고 있죠! 이미 추가해봤잖아요.)

// 리듀서예요.
// 실질적으로 store에 들어가 있는 데이터를 변경하는 곳이죠!
export default function reducer(state = initialState, action = {}) {
                                // state = {} -> 기본값(초깃값)을 준다.
  switch (action.type) {
    // do reducer stuff
    case "bucket/LOAD":
      return state;

    case "bucket/CREATE":
      const new_bucket_list = [...state.list, action.bucket];
      return { list: new_bucket_list };

    default:
      return state;
  }
}

 

(5) Store

: redux 폴더 하위에 있는 configStor.js 파일을 만들고 그 안에 스토어를 만들어 줍니다.

import {createStore, combineReducers} from 'redux';
// combineReducers : 리듀서를 묶는 역할을 하는 함수
import bucket from './modules/bucket';

const rootReducer = combineReducers({bucket});
// rootReducer : 리듀스들을 다 묶은 것을 말한다.
// rootReducer을 만들어 줍니다.
// -> 나중에 리듀서를 여러 개 만들게 되면 여기에 하나씩 추가 해줍니다.

// 스토어를 만듭니다.
const store = createStore(rootReducer);

export default store;
// 만든 스토어를 수출 해준다.

2. 리덕스와 컴포넌트 연결하기

 

(1) Store 연결하기

: store를 다 만든 후에는 컴포넌트와 연결을 해야 합니다.(구독한다고 합니다.)

index.js에서 필요한 작업을 해주겠습니다.

-> 리듀서들과 필요한 옵션을 묶어서 만든 스토어를 불러오고 -> 우리의 버킷리스트에 주입하면 끝납니다.

 

-> index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {BrowserRouter} from "react-router-dom";

// 우리의 버킷리스트에 리덕스를 주입해줄 프로바이더를 불러옵니다!
import { Provider } from "react-redux";
// 연결할 스토어도 가지고 와요.
import store from "./redux/configStore";

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

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

 

(흐름 정리)

(1) 리덕스 모듈을 만든다. (액션 타입, 액션 생성자, 리듀서를 덕스 구조로 묶는다, 다른 방식도 OK)

(2) 만든 모듈 안에 있는 리듀서를 묶어서 rootReducer를 만들고 rootReducer를 가지고 Store를 만든다.

(3) 만든 스토어를 컴포넌트가 구독하도록 한다.


3. 컴포넌트에서 리덕스 데이터 사용하기

 

- 컴포넌트에서 리덕스 액션 사용하는 방법

: 이제 우리 리덕스가 버킷리스트에 붙었는데, 아직 실감이 안나죠?

실감이 나도록, App 컴포넌트에 있는 state를 리덕스로 교체해볼까요?

 

(1) 컴포넌트에서 리덕스 데이터 사용하기

 

- 리덕스 훅

: 리덕스도 훅이 있어요!

상태, 즉, 데이터를 가져오는 것 하나 -> useSelector

상태를 업데이트할 수 있는 것 하나 -> useDispatch

🙂 이렇게 두 가지를 정말 많이 쓴답니다! 

// useDispatch는 데이터를 업데이트할 때,
// useSelector는 데이터를 가져올 때 씁니다.
import {useDispatch, useSelector} from "react-redux";

 

useSelector((state) ⇒ state.bucket)

: configStore.js에서 루트 리듀서를 만들었던 거 기억하시나요? 

여기에서 state는 리덕스 스토어가 가진 전체 데이터예요.

 

- BucketList.js에서 redux 데이터 가져오기

// redux 훅 중, useSelector를 가져옵니다.
import { useSelector } from "react-redux";

const BucketList = (props) => {
  let history = useHistory();
  //   이 부분은 주석처리!
  //   console.log(props);
  //   const my_lists = props.list;
  // 여기에서 state는 리덕스 스토어가 가진 전체 데이터예요.
  // 우리는 그 중, bucket 안에 들어있는 list를 가져옵니다.
  const my_lists = useSelector((state) => state.bucket.list);
  return (
    <ListStyle>
      {my_lists.map((list, index) => {
        return (
          <ItemStyle
            className="list_item"
            key={index}
            onClick={() => {
              history.push("/detail");
            }}
          >
            {list}
          </ItemStyle>
        );
      })}
    </ListStyle>
  );
};

 

- App.js에서 redux 데이터 추가하기

 

-> import 먼저! (dispatch를 발생 시키기 위해서는 액션이 필요하기 떄문에 액션 생성 함수도 import!)

// useDispatch를 가져와요!
import {useDispatch} from "react-redux";
// 액션생성함수도 가져오고요!
import { createBucket } from "./redux/modules/bucket";

-> useDispatch 훅 쓰기

const dispatch = useDispatch();

const addBucketList = () => {
    // 스프레드 문법! 기억하고 계신가요? :)
    // 원본 배열 list에 새로운 요소를 추가해주었습니다.
    // 여긴 이제 주석처리!
    // setList([...list, text.current.value]);

    dispatch(createBucket(text.current.value));
  };

 

(2) 상세페이지에서 버킷리스트 내용을 띄워보기

 

-> (첫번째) 어떤 게시물을 눌렀는 지 알아야 한다!

-> (두번째) 뭐를 눌렀는 지 알았다면 뭐가 눌렸는 지 보여줘야 한다.

 

- 몇번째 상세에 와있는 지 알기 위해서 URL 파라미터를 적용하자

// App.js
<Route exact path="/detail/:index" component={Detail} />
//BucketList.js
...
      {my_lists.map((list, index) => {
        return (
          <ItemStyle
            className="list_item"
            key={index}
            onClick={() => {
              history.push("/detail/"+index);
            }}
          >
            {list}
          </ItemStyle>
        );
      })}
...

- 상세페이지에서 버킷리스트 내용을 띄워보자

//Detail.js

// 리액트 패키지를 불러옵니다.
import React from "react";
// 라우터 훅을 불러옵니다. (파라미터 URL의 값을 불러오기 위해서)
import {useParams} from "react-router-dom";
// redux hook을 불러옵니다.
// (redux의 데이터를 불러온다.)
import { useSelector } from "react-redux";

const Detail = (props) => {

  // 스토어에서 상태값 가져오기
  const bucket_list = useSelector((state) => state.bucket.list);
  // (state를 사용해서 store의 모든 데이터를 가져온다)
  // -> (bucket 모듈에 딕셔너리 형태인 list의 데이터를 가져온다.)
  
  // url 파라미터에서 인덱스 가져오기
  const params = useParams();
  // params가 딕셔너리 형태이기 때문에 .index를 사용해서 index 값만 불러온다.
  const bucket_index = params.index;

  return <h1>{bucket_list[bucket_index]}</h1>;
};

export default Detail;

댓글