본문 바로가기
내마음대로만들어보자/React

리덕스(redux) 써보기

by 소농민! 2021. 7. 26.
728x90

1. 덕스(ducks)구조 

보통은 리덕스를 사용할때는 모양대로 구분해서 코드를 작성해준다.

(액션은 액션대로, 액션생성함수는 액션생성함수끼리, 리듀서는 리듀서끼리 작성)

 

덕스구조는 모양새대로 묶는것이 아닌 기능대로 묶어주는것을 말한다.

리덕스 기본형태를 아래 깃허브를 참고하자.

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

// 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)))
}

기본형태는 외울필요 없이 구조가 어떻게 생겼는지정도만 파악해두고 가져다가 쓰자! 

자주보고 사용하다보면은 자연스럽게 익혀질것이다!

 

2. bucke_list 에 리덕스를 사용해보자.

 

우선, src 폴더 아래 redux 폴더 그 아래 modules 폴더를 만들어 bucket.js 를 만들자!

다음과 같은 과정으로 버킷리스트 항목을 리덕스에서 관리하도록 변경해보자.(작업 순서를 이해하면서 복습하자.)

 

1. Action : 액션은 처음 로드되었을때, 버킷리스트 항목을 추가했을때 발생한다.

   const LOAD = 'bucket/LOAD';
   const CREATE = 'bucket/CREATE';

 

2. initialState : 초기 상태값을 만들어준다. (= 기본값)

const initialState = {
        list: ["영화관 가기", "매일 책읽기", "수영 배우기"],
  };

 

3. Action Creactor : 액션 생성함수 

  export const loadBucket = (bucket) => {
             return { type: LOAD, bucket };
    }
    export const createBucket = (bucket) => {
            return {type: CREATE, bucket};
   }

 

4. Reducer : load할때는 기본값을 뿌려주면되고, create 할때는 새로 받아온 값을 기본값에 더해서 리턴

                      (action으로 넘어오는 값은 text)

    export default function reducer(state = initialState, action = {}) {
             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 폴더 하위에 configStore.js 파일을 만들어서 스토어를 만들어보자.

//configStore.js
import { createStore, combineReducers } from "redux";
import bucket from './modules/bucket';
import { createBrowserHistory } from "history";
// 브라우저 히스토리를 만들어준다.

export const history = createBrowserHistory();
// root 리듀서를 만들어준다.
// 나중에 리듀서를 여러개 만들게 되면 여기에 하나씩 추가해주면된다.

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

export default store;

 

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

index.js 에 필요한 작업을 하면되며, 스토어를 불러오고 버킷리스트에 주입하면 끝!

4. 클래스형 컴포넌트에서 리덕스 데이터 사용하기   

    1) 리덕스 모듈과 connect 함수를 불러온다.

        import {connect} from 'react-redux';

        import {loadBucket, createBucket} from './redux/modules/bucket';

 

    2) 상태값을 가져오는 함수와 액션 생성 함수를 부르는 함수를 만들어준다.

// 이 함수는 스토어가 가진 상태값을 props로 받아오기 위한 함수.
const mapStateToProps = (state) => ({
               bucket_list: state.bucket.list,
          });

// 이 함수는 값을 변화시키기 위한 액션 생성 함수를 props로 받아오기 위한 함수.
const mapDispatchToProps = (dispatch) => ({
                    load: () => {
                       dispatch(loadBucket());
                    },
                    create: (new_item) => {
                       dispatch(createBucket(new_item));
                    }
                });

    3) connect로 컴포넌트와 스토어를 엮어준다

        export default connect(mapStateToProps, mapDispatchToProps)(withRouter(App));

    4) 콘솔에 this.props를 찍어봅니다. (스토어에 있는 값이 잘 나왔는지 확인)

        componentDidMount() {

             console.log(this.props);

         }

    5) this.state에 있는 list를 지우고 스토어에 있는 값으로 바꿔보자.

    6) setState를 this.props.create로 바꿔보자.

        addBucketList = () => {

             const new_item = this.text.current.value;

             this.props.create(new_item);

         };

 

5. 함수형 컴포넌트 리덕스 데이터 사용하기

 - 리덕스에도 리액트처럼 훅을 사용할 수 있다. 훅을 사용해서 액션 생성 함수도 불러오고, 스토어에 있는 값도 가져올 수 있다.

 

1) BucketList.js 에 useSelector() 적용하기 

// redux hook을 불러온다
import {useDispatch, useSelector} from 'react-redux';

const BucketList = (props) => {
// 버킷리스트를 리덕스 훅으로 가져오기
         const bucket_list = useSelector(state => state.bucket.list);

console.log(bucket_list);

2) App.js에 몇번째 상세페이지인지 알아야 하므로 URL 파라미터를 적용해보자. 

<Switch>
     <Route
        path="/"
        exact
        render={(props) => <BucketList history={props.history} />}
      />
     
      <Route
         path="/detail/:index"
         render={(props) => <Detail match={props.match} history={props.history} />}
       />
       // match 객체는 해당 컴포넌트가 어떤 라우트에 매치되어 있는지에 대한 정보가 있고, 파라메터 정보를 params 속성으로 지닌다


      <Route
          render={(props) => <NotFound history={props.history} />}
      />
</Switch>

3) BucketList.js 에 onClick 함수에 배열의몇번째 항목을 눌렀는지 알려주자(url 파라미터로 넘겨주면된다.)

{bucket_list.map((list, index) => {
         return (
              <ItemStyle
                  className="list_item"
                  key={index}
                  onClick={() => {
                  // 배열의 몇번째 항목을 눌렀는 지, url 파라미터로 넘겨줍니다.
                  props.history.push("/detail/"+index);
               }}
             >
                 {list}
              </ItemStyle>
         );
    })}
  </ListStyle> 
  );
}; 

4) Detail.js (상세페이지)에서 버킷리스트 내용을 띄워보자.

// 리액트 패키지를 불러온다.
import React from "react";

// redux hook을 불러온다.
import { useDispatch, useSelector } from "react-redux";

const Detail = (props) => {
         // 스토어에서 상태값 가져오기
         const bucket_list = useSelector((state) => state.bucket.list);
        // url 파라미터에서 인덱스 가져오기
       let bucket_index = parseInt(props.match.params.index);

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

export default Detail;

 

 

※ 버킷리스트 데이터를 삭제해보기!

 

1. Detail.js 에 삭제버튼 만들고, 액션생성함수 만들기

...
return (
    <div>
        <h1>{bucket_list[bucket_index]}</h1>
       <button onClick={() => {
         // dispatch(); <- 괄호안에는 액션 생성 함수가 들어가면된다.
        dispatch(deleteBucket(bucket_index));
        // 삭제 후 그 페이지에 두지말고 뒤로가기를 통해 원래 화면으로 넘어가주게 한다. 사용성을 위해
        props.history.goBack();
      }}>삭제하기</button>
     </div>
  );
 };
export default Detail;

2. bucket.js 에 액션생성함수, 액션, 리듀서를 만들어준다.

  - Action 

    const DELETE = "bucket/DELETE";

 

  - Action Creator 

    export const deleteBucket = (bucket) => {

           return { type: DELETE, bucket };

    };

 

  - Reducer

    case "bucket/DELETE":

           const bucket_list = state.list.filter((l, idx) => {

                if(idx !== action.bucket){

                   return l;

            }

       });

      return {list: bucket_list};

 

이렇게 하면은 정상적으로 삭제되고 버킷리스트 목록으로 넘어가는걸 볼 수 있다.

우선은 어떻게 동작하는지 원리를 파악하는데 중점을 두자!