새소식

기술 습관/React

[프로그래머스] 타입스크립트로 함께하는 웹 풀 사이클 개발 12-2

  • -

리덕스 Hooks 생성하기

  • 들어도 어려움.. 복습 필요

Slicer

ModalSlice

import { createSlice } from "@reduxjs/toolkit";
import { ITask } from "../../types";

type TModalState = {
  boardId: string;
  listId: string;
  task: ITask;
};

const initialState: TModalState = {
  boardId: "board-0",
  listId: "list-0",
  task: {
    taskId: "task-0",
    taskName: "task 0",
    taskDescription: "task description",
    taskOwner: "hee",
  },
};

const modalSlice = createSlice({
  name: "modal",
  initialState,
  reducers: {

  }
});

export const modalReducer = modalSlice.reducer;

loggerSlice

import { createSlice } from "@reduxjs/toolkit";
import { ILogItem } from '../../types'

type loggerState = {
  logArray: ILogItem[]
}

const initialState: loggerState = {
  logArray: []
}

const loggerSlice = createSlice({
  name: 'logger',
  initialState,
  reducers: {

  }

})

export const loggerReducer = loggerSlice.reducer;

boardSlice

import { createSlice } from "@reduxjs/toolkit";
import { IBoard } from "../../types";

type TBoardState = {
  modalActive: boolean;
  boardArray: IBoard[]
}

const initialState: TBoardState = {
  modalActive: false,
  boardArray: [
    {
      boardId: 'board-0',
      boardName: '첫 번째 게시물',
      lists: [
        {
          listId: 'list-0',
          listName: 'List 1',
          tasks: [
            {
              taskId: 'task-0',
              taskName: 'Task 1',
              taskDescription: 'description',
              taskOwner: 'hee'
            },
            {
              taskId: 'task-1',
              taskName: 'Task 2',
              taskDescription: 'description2',
              taskOwner: 'hee'
            }
          ]
        },
        {
          listId: 'list-1',
          listName: 'List 2',
          tasks: [
            {
              taskId: 'task-2',
              taskName: 'Task 3',
              taskDescription: 'description3',
              taskOwner: 'hee'
            },
          ]
        }
      ]
    }
  ]
}

const boardSlice = createSlice({
  name: 'boards',
  initialState,
  reducers: {

  }
})

export const boardsReducer = boardSlice.reducer;

types

index.ts

import { createSlice } from "@reduxjs/toolkit";
import { IBoard } from "../../types";

type TBoardState = {
  modalActive: boolean;
  boardArray: IBoard[]
}

const initialState: TBoardState = {
  modalActive: false,
  boardArray: [
    {
      boardId: 'board-0',
      boardName: '첫 번째 게시물',
      lists: [
        {
          listId: 'list-0',
          listName: 'List 1',
          tasks: [
            {
              taskId: 'task-0',
              taskName: 'Task 1',
              taskDescription: 'description',
              taskOwner: 'hee'
            },
            {
              taskId: 'task-1',
              taskName: 'Task 2',
              taskDescription: 'description2',
              taskOwner: 'hee'
            }
          ]
        },
        {
          listId: 'list-1',
          listName: 'List 2',
          tasks: [
            {
              taskId: 'task-2',
              taskName: 'Task 3',
              taskDescription: 'description3',
              taskOwner: 'hee'
            },
          ]
        }
      ]
    }
  ]
}

const boardSlice = createSlice({
  name: 'boards',
  initialState,
  reducers: {

  }
})

export const boardsReducer = boardSlice.reducer;

store

/store/index.ts

// /store/index.ts
// redux 사용할 때 redux store를 만들어줘야하는데 여기에서 만들게 됨
import { configureStore } from "@reduxjs/toolkit";
import reducer from "./reducer/reducer";
import { useSelector } from 'react-redux';

const store = configureStore({
  reducer
});

export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch;

// const logger = useSelector((state: RootState)=> state.logger);
// 에러발생
/*
    chunk-EPSXJ6EN.js?v=a2c9f6bb:1062 Uncaught TypeError: Cannot read properties of null (reading 'useContext')
    at Object.useContext (chunk-EPSXJ6EN.js?v=a2c9f6bb:1062:29)
    at useReduxContext2 (react-redux.js?v=2b6e48a7:146:32)
    at useSelector2 (react-redux.js?v=2b6e48a7:184:9)
    at index.ts:13:16

    React Hook "useSelector" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function.eslintreact-hooks/rules-of-hooks
*/

export default store;

redux.ts

// /hooks/redux.ts
// redux를 위한 hooks eg. useSelector =>  / useDispatcher =>
import { TypedUseSelectorHook, useSelector } from "react-redux";
import { useDispatch } from "react-redux";
import { AppDispatch, RootState } from "../store";

/**
 * TypedUseSelectorHook<RootState> 타입으로 정의해야하는 이유
 * RootState로 값을 반환할 때 {state: {..RootState}} 형태로 만들어주기 위해
 */
export const useTypedSelector: TypedUseSelectorHook<RootState> = useSelector
export const useTypedDispatch = () => useDispatch<AppDispatch>();

// const logger = useTypedSelector(state => state.logger);

Main

main.tsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import { Provider } from 'react-redux'
import store from './store/index';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
)

CSS 관련

App.tsx

import { appContainer, board, bottons } from './App.css'

function App() {

  return (
    <div className={appContainer}>
      <div className={board}>
        test
      </div>
      <div className={bottons}>
        <button>
          이 게시판 삭제하기
        </button>
        <button>

        </button>
      </div>
    </div>
  )
}

export default App

App.css.ts

// vanilla-extract 이용
// - 원활한 사용을 위해 vite-config.ts에 vanillaExtractPlugin() 플러그인 추가 필수

import { createGlobalTheme, style } from "@vanilla-extract/css";

export const vars = createGlobalTheme("root", {
  color: {
    main: "#265cff",
    mainDarker: "#243f8f",
    mainFaded: "#cdd9ff",
    mainFadedBright: "#ebf0ff",
    list: "rgba(235, 236, 240, 0.5)",
    task: "#f1f1f1",
    taskHover: "#cdd9ff",
    brightText: "#212121",
    darkText: "#f1f1f1",
    secondaryDarkText: "#c8c8c8",
    secondaryDarkTextHover: "#d4d3d3",
    selectedTab: "rgb(137, 176, 174)",
    updateButton: "#4c7097",
    deleteButton: "#c04959",
  },
  fontSizing: {
    T1: "32px",
    T2: "24px",
    T3: "18px",
    T4: "14px",
    P1: "12px",
  },
  spacing: {
    sm: "4px",
    md: "8px",
    big1: "24px",
    big2: "16px",
    listSpacing: "32px",
  },
  font: {
    body: "arial",
  },
  shadow: {
    basic: "4px 4px 8px 0px rgba(34, 60, 80, 0.2)",
  },
  minWidth: {
    list: "250px",
  },
});

export const appContainer = style({
  display: "flex",
  flexDirection: "column",
  minHeight: "100vh",
  height: "max-content",
  width: "100vw",
});

export const board = style({
  display: "flex",
  flexDirection: "row",
  height: "100%",
});
export const bottons = style({
  marginTop: "auto",
  paddingLeft: vars.spacing.big2,
});

vite.config.ts

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react(), vanillaExtractPlugin()],
})
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.