Codesandbox나 Codepen에서 React-Redux로 구현된 다양한 예제들이 많이 보인다. 각자만의 방식으로 작성되어있어 내 것으로 만들기에는 약간 시간이 필요했었다. 그래서 이번에 나만의 방법으로 React-Redux와 Hook을 활용해 Tab을 구현해 보려한다.
우선 Redux의 작동원리를 다시 알아보자면 간단하게는 아래와 같다.
사용자는 어떠한 입력을 하게되면 dispatch를 통해 action을 Reducer로 전달한다. Reducer는 정해진 타입에따라 Store내의 State의 저장된 상태값을 바꿔 반환한다. dispatch, State 등 다양한 일을 관리해주는 핵심 요소가 바로 Store라는 곳이다.
store폴더를 만들어 작성하거나 아니면 가장 최상단 컴포넌트에 작성해준다.
import React from "react";
import ReactDOM from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import thunk from "redux-thunk";
const middleware = [thunk];
const store = createStore(
rootReducer,
composeWithDevTools(applyMiddleware(...middleware))
);
ReactDOM.render(
<Provider store={store}>
<GlobalStyles />
<MainContent />
</Provider>,
document.getElementById("root")
);
Provider : Provider 컴포넌트는
React-redux라이브러리에서 제공되는 컴포넌트로React가store에 쉽게 연동되게 도와주는 역할을 한다.
thunk : thunk 는 리덕스에서 비동기 작업을 위해 사용하는 미들웨어이고, 액션 객체가 아닌 함수를 디스패치할 수 있다. 현재 사용하는 이유는 아직까지는
Redux 개발툴만 사용하기위해 적용함.
export const TABS = {
ACTIVE_TAB: "ACTIVE_TAB",
};
export const activeTabs = (tabs) => {
return { type: TABS.ACTIVE_TAB, activeTab: tabs };
};
activeTab의 역할은 선택한 요소의 인덱스를 전달해주는 역할이다.
import { combineReducers } from "redux";
import { TABS } from "../_actions/types";
const initialState = {
activeTab: 0,
};
export default function tabs_Reducer(state = initialState, action) {
switch (action.type) {
case TABS.ACTIVE_TAB:
return {
...state,
activeTab: action.activeTab,
};
default:
return state;
}
}
const rootReducer = combineReducers({
tabs_Reducer,
});
export default rootReducer;
combineReducers는 액션에 따른 다양한 reducer를 작성하게 되는데, 이를 합쳐 하나의 Reducer로 관리 할 수 있게 해준다.
import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { activeTabs } from "../../_actions/tab_action";
import {
StyledMainContainer,
DefaultScreen,
TabList,
TabMenu,
TabContainer,
} from "./styles/StyledTabContent";
const tabTitle = ["탭1", "탭2", "탭3", "탭4", "탭5"];
const tabContent = {
0: <ContentA />,
1: <ContentB />,
2: <ContentC />,
3: <ContentD />,
};
export default function MainContent() {
const dispatch = useDispatch();
const activeTab = useSelector((state) => state.tabs_Reducer.activeTab);
console.log(useSelector((state) => state.tabs_Reducer.activeTab));
return (
<StyledMainContainer>
<DefaultScreen>
<TabContainer>
<TabList>
{tabTitle.map((menu, idx) => (
<TabMenu key={idx} onClick={() => dispatch(activeTabs(idx))}>
{menu}
</TabMenu>
))}
</TabList>
{tabContent[activeTab]}
{console.log(tabContent[activeTab])}
</TabContainer>
</DefaultScreen>
</StyledMainContainer>
);
}
useDispatch()는 Hook이전에 액션을 발생시키려면mapStateToProps()를 사용해줬었다. 이제는useDispatch()를 사용하여 간편하게 구현이 가능하다.
useSelector()는Store내의state를 조회하기 위한 함수이다. 위 코드에서useSelector((state) => state.tabs_Reducer)이부분을 콘솔로그로 찍어보면state에 저장된 상태를 조회 할 수 있다.
반복 작업을 줄이기 위해 map 메소드를 사용했으며 tabTitle중 하나를 클릭 할 경우 index가 변경된다. 해당 index에 따른 컴포넌트들을 렌더링을 해준다.
이렇게 간단한 tab이 완성되었다.