| 일 | 월 | 화 | 수 | 목 | 금 | 토 | 
|---|---|---|---|---|---|---|
| 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 | 
- typescript
- multiple camera
- localization
- jszip
- Redux
- swagger-typescript-api
- append row
- babel
- M3U8
- uint16array
- webrtc
- http live streaming
- how to install cursor on ubuntu
- REST API
- Game js
- HLS
- uint8array
- three.js
- hls.js
- html2canvas
- node
- Flutter
- KakaoMap
- Babel standalone
- segment
- cursor-ubuntu-installer
- code editor
- Prism.js
- Excel
- react
- 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.reducersrc/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.reducercreateSlice에 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 MainIndexsrc/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 CounterIndexuseSelector를 이용해서 원하는 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 Dialogsrc/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 | 
 
								 
								 
								