일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- methodChannel
- jszip
- babel
- Redux
- androidId
- node
- Prism.js
- Excel
- Game js
- Completer
- KakaoMap
- swagger-typescript-api
- REST API
- Image Resize typescript
- typescript
- Babel standalone
- Three-fiber
- Three js
- Raycasting
- FirebaseAnalytics
- webrtc
- uint8array
- code editor
- RouteObserver
- uint16array
- react
- web track
- Flutter
- userevent_tracker
- identifierForVender
- Today
- Total
Never give up
React - redux toolkit example(createStore is deprecated) 본문
간만에(?) 개발하던 웹 소스를 봤는데 createStore가 deprecated되었더군요..
밑에 내용을 확인해보니 redux toolkit에 있는 configureStore로 하라고 해서 사용법을 찾아봤습니다
(링크: https://redux-toolkit.js.org/)
필자가 사용하던 createStore와 reducer가 configureStore, createSlice로 대체됐습니다
createStore -> configureStore
reducer -> createSlice
src/store.js
import { configureStore } from "@reduxjs/toolkit"
import counter from "slices/counter"
import dialog from "slices/dialog"
import toast from "slices/toast"
export default configureStore({
reducer: {
counter,
dialog,
toast
}
})
configureStore속성중 reducer에 만들어놓은 reducer를 넣어줍니다
이전에는 combineReducers에 넣어줬던것과 비슷한데 조금 더 편한거 같습니다
src/slices/toast.js
import { createSlice } from "@reduxjs/toolkit";
const toastSlice = createSlice({
name: 'toast',
initialState: { show: false, message: '' },
reducers: {
show: (state, action) => {
if (state.show) {
state.show = false
state.message = ''
}
state.show = true
state.message = action.payload
},
hide: (state) => {
state.show = false
state.message = ''
}
}
})
export const { show, hide } = toastSlice.actions
export default toastSlice.reducer
src/slices/dialog.js
import { createSlice } from "@reduxjs/toolkit";
const dialogSlice = createSlice({
name: 'dialog',
initialState: { value: false },
reducers: {
open: (state) => {
state.value = true
},
close: (state) => {
state.value = false
}
}
})
export const { open, close } = dialogSlice.actions
export default dialogSlice.reducer
src/slices/counter.js
import { createSlice } from "@reduxjs/toolkit";
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => {
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
setValue: (state, action) => {
state.value = action.payload
}
}
})
export const { increment, decrement, setValue } = counterSlice.actions
export default counterSlice.reducer
createSlice에 name, initialState, reducers를 넣어줍니다
기존에는 reducer에 switch로 action에 따라서 반환하는 state값을 다르게 해줬었는데
훨씬더 간단해진거 같습니다
필자가 사용한 slice는 총 세가지로
- 화면에 표시되는 숫자 상태변경
- dialog 상태 토글
- toast 메시지 상태
src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from 'App';
import reportWebVitals from 'reportWebVitals';
import { Provider } from 'react-redux';
import store from 'store'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
reportWebVitals();
store를 provider에 넣어주는 부분은 동일합니다
src/app.js
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import CounterIndex from 'routes/counter';
import MainIndex from 'routes/main';
import { ToastComponent } from 'components/toast';
function App() {
const routes = [
{ path: '/', element: <MainIndex /> },
{ path: '/counter', element: <CounterIndex /> }
]
return (
<BrowserRouter>
<Routes>
{routes.map((e) => (<Route key={`route - ${e.path}`} path={e.path} element={e.element} />))}
</Routes>
<ToastComponent />
</BrowserRouter>
);
}
export default App;
BroswerRouter내부에 Toast를 넣은 이유는
나중에(?) Toast에 링크를 만들고 navigate기능을 넣을 때 사용하면 좋을것 같아서 넣어봤습니다
src/routes/main/index.jsx
import React from 'react'
import { useNavigate } from 'react-router-dom'
import styles from './index.module.css'
function MainIndex() {
const navigate = useNavigate()
const pages = [
{ title: 'counter', link: '/counter' }
]
return (
<main className={styles.main}>
{pages.map((e, _) => {
return (
<div key={`div-${e.title}`} onClick={() => navigate(e.link)}>
{e.title}
</div>
)
})}
</main>
)
}
export default MainIndex
src/routes/main/index.module.css
.main {
height: 100vh;
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
}
.main>div{
cursor: pointer;
}
.main>div:hover{
color: rgba(0, 0, 0, 0.7);
}
나중에 예제 여러개 만들어서 사용하기 좋게 만들어봤습니다
(사실 중앙에 고작 하나 보여주는데 이렇게까지..)
src/routes/counter/index.jsx
import Dialog from 'components/dialog'
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { decrement, increment, setValue } from 'slices/counter'
import { open } from 'slices/dialog'
function CounterIndex() {
const count = useSelector((state) => state.counter.value)
const showDialog = useSelector((state) => state.dialog.value)
const dispatch = useDispatch()
return (
<div style={style}>
<div>
<p>{count}</p>
<button
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
+
</button>
<button
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
-
</button>
<button
aria-label="Set value"
onClick={() => dispatch(open())}
>
값 설정
</button>
{showDialog ? <Dialog setValue={(value) => dispatch(setValue(value))} /> : <></>}
</div>
</div>
)
}
const style = { height: '100vh', width: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center', textAlign: 'center' }
export default CounterIndex
useSelector를 이용해서 원하는 state를 가져오고
dispatch를 통해서 원하는 action을 호출해줍니다
(inline style로 만든건 귀찮아서 손이 멋대로...)
src/components/dialog.jsx
import React, { useState } from 'react'
import styles from 'components/dialog.module.css'
import { useDispatch } from 'react-redux'
import { close } from 'slices/dialog'
import { showToast } from './toast'
function Dialog(props) {
const dispatch = useDispatch()
const [number, setNumber] = useState()
const confirm = () => {
if (number === undefined || number === '') {
showToast({ message: '값을 입력해주세요', dispatch })
return
}
props.setValue(number)
dispatch(close())
showToast({ message: `${number}로 변경되었습니다.`, dispatch })
}
return (
<div className={styles.div_background}>
<div className={styles.div_dialog}>
<p>값을 입력해주세요</p>
<input type='number' onChange={(e) => setNumber(Number(e.target.value))} />
<div className={styles.div_actions}>
<button onClick={() => dispatch(close())}>취소</button>
<button onClick={confirm}>설정</button>
</div>
</div>
</div>
)
}
export default Dialog
src/components/dialog.module.css
.div_background {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
z-index: 2;
background-color: rgba(0, 0, 0, 0.7);
}
.div_dialog {
padding: 20px;
text-align: center;
border-radius: 20px;
background-color: white;
}
.div_dialog>p {
font-size: 18px;
margin: 0;
text-align: center;
}
.div_dialog>input {
border: 1px solid grey;
margin: 20px 0 20px 0;
}
.div_actions {
display: flex;
flex-direction: row;
justify-content: space-around;
}
.div_actions>button {
border: 0;
background-color: transparent;
}
.div_actions>button:hover {
color: gray;
}
다이얼로그에서는 값 입력부분은 useState로 처리를 했고
이후에 showToast로 값 변경 혹은 문제가 발생하면 알림을 주는 형태로 만들어봤습니다
src/components/toast.jsx
import React from 'react'
import { useSelector } from 'react-redux'
import { hide, show } from 'slices/toast'
import styles from './toast.module.css'
function ToastComponent() {
const { show, message } = useSelector((state) => state.toast)
return (
<>
{show ? <div className={styles.toast}>
{message}
</div> : <></>}
</>
)
}
const showToast = ({ message, dispatch }) => {
dispatch(show(message))
setTimeout(() => {
dispatch(hide())
}, 2000);
}
export { ToastComponent, showToast }
src/components/toast.module.css
.toast {
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-color: rgba(0, 0, 0, 0.6);
color: white;
border-radius: 12px;
padding: 10px 20px 10px 20px;
animation: fade 2.0s ease;
z-index: 999;
}
@keyframes fade {
0%,
100% {
opacity: 0;
}
30%,
80% {
opacity: 1;
}
}
마지막으로 toast는 간단하게 만들어봤는데
useEffect로 state변경에 따라서 hide하는 부분을 만들어도 괜찮았던것 같았습니다
(전체 소스코드 : https://github.com/devmemory/react_redux_example)
--- 마지막으로.. 필자의 헛소리
이런저런 자료들 찾아보면서 생각나는대로 코딩하고 있는데.. 잘 하고 있는건지 잘 모르겠습니다
일단 결과물은 나오고 있는데 너무 막 코딩하다보면
나중에 웹프론트로 이직할 때 문제가 생기지 않을지 걱정됩니다..
그리고 css 화면 배치는 어느정도 익숙해진것 같은데 애니메이션은 아직 한참 멀은거 같습니다..
한동안 개인공부 시간이 줄어들어서 진도가 잘 안나가고 있는데 시간이 좀 더 생기면 더 열심히 해야될거 같습니다
'WEB' 카테고리의 다른 글
React, Node - Graphql example(feat. apollo) (0) | 2022.05.24 |
---|---|
React - Without CRA (0) | 2022.05.23 |
Node - XLSX example (feat. scraping) (0) | 2022.05.11 |
Node - Nodemailer example (2) | 2022.04.26 |
Node - Google analytics (feat. Chart.js, Excel) (0) | 2022.04.22 |