[TS] ๐ŸงคJS๋กœ ์ž‘์„ฑ๋œ React ํ”„๋กœ์ ํŠธ๋ฅผ TS๋กœ ๋ฐ”๊พธ๋Š” ๋ฐฉ๋ฒ•

TATAยท2023๋…„ 6์›” 1์ผ
0

TypeScript

๋ชฉ๋ก ๋ณด๊ธฐ
4/4

โ–ท typescript ํ™˜๊ฒฝ ์„ค์ •

CRA๋กœ typescript ์‹œ์ž‘ํ•  ์‹œ

npx create-react-app my-app --template typescript

๊ธฐ์กด javascript๋กœ ์ž‘์„ฑ๋œ React ํ”„๋กœ์ ํŠธ์— typescript๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฒŒ ๋  ๊ฒฝ์šฐ

npm install typescript @types/node @types/react @types/react-dom @types/jest @types/react-router-dom

# ๋ฆฌ๋•์Šค ํˆดํ‚ท
npm install --save-dev @reduxjs/toolkit @types/react-redux

# ์Šคํƒ€์ผ๋“œ ์ปดํฌ๋„ŒํŠธ
npm install @types/styled-components

# ์Šคํ† ๋ฆฌ๋ถ
npm install --save-dev @storybook/preset-typescript

# tsconfig.json ์ƒ์„ฑ
npx tsc --init

# npm install @types/react๋ฅผ ํ•ด์คฌ๋Š”๋ฐ๋„ ์•ˆ ๋– ์„œ ๋‹ค์‹œ ์„ค์น˜ํ•จ
# npm install --save-dev @types/react

๐Ÿงค tsconfig.json์— ๋‚ด์šฉ ์ถ”๊ฐ€

"compilerOptions": {
  "jsx": "react",
  "types": ["styled-components"],
}

๐Ÿงค index.tsx

const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);

root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

๐Ÿงค svg, png... ์ด๋ฏธ์ง€ ํŒŒ์ผ ์ธ์‹ ๋ชปํ•˜๋Š” ๊ฒฝ์šฐ

srcํด๋” ์•ˆ์— custom.d.tsํŒŒ์ผ ์ƒ์„ฑ

/* src/custom.d.ts */

declare module "*.svg" {
  const content: any;
  export default content;
}

declare module "*.png" {
  const content: any;
  export default content;
}

tsconfig.json์— ๋‚ด์šฉ ์ถ”๊ฐ€

/* tsconfig.json */

"include": ["src", "src/custom.d.ts"],
"exclude": ["node_modules"]

โ–ท Redux์—์„œ typescript ์‚ฌ์šฉ๋ฒ•

๐Ÿงค Store

Store/index.ts

/* Store/index.ts */
import { legacy_createStore as createStore, combineReducers } from "redux";
import { todoReducer } from "../Reducers";

const rootReducers = combineReducers({ todoReducer });
export type RootState = ReturnType<typeof rootReducers>; // ์—ฌ๊ธฐ ์ถ”๊ฐ€

const store = createStore(rootReducers);

export default store;

๐Ÿงค Actions

Actions/action.tsx

โ’ˆ ํƒ€์ž…์ง€์ • ๋ฐ as ์ง€์ •

export const CREATE: string = "create" as const;

โ’‰ enum์œผ๋กœ ํƒ€์ž… ์ง€์ •

export enum ActionTypes {
  CREATE = "create",
  REMOVE = "remove",
  TOGGLE = "toggle",
  DOING_FILTER = "doingFilter",
  COMPLE_FILTER = "compleFilter",
  CHANGE_TEXT = "changeText",
}

โ’Š ์•ก์…˜ ํ•จ์ˆ˜์— ๋งž๊ฒŒ ๋ฆฌํ„ด ํƒ€์ž… ์ง€์ •

// ์ธํ„ฐํŽ˜์ด์Šค
export interface ITodos {
  id: number;
  text: string;
  done: boolean;
}

interface ICreateAction {
  type: ActionTypes.CREATE;
  payload: ITodos;
}
.....

// ์•ก์…˜ ํ•จ์ˆ˜
export const create = (todo: ITodos): ICreateAction => {
  return {
    type: ActionTypes.CREATE,
    payload: todo,
  };
};
.....

// ์•ก์…˜ ํƒ€์ž…๋“ค
export type TTodoAction = ICreateAction | IRemoveAction | IToggleAction | IDoingFilterAction | ICompleFilterAction | IChangeTextAction;

๐Ÿงค Reducers

Reducers/initialState.js

mport { ITodos } from "../Actions/action";

export interface ITodosArr {
  todos: ITodos[];
  doing: ITodos[];
  completed: ITodos[];
}

export const initialState: ITodosArr = {
  todos: [
    {
      id: 1,
      text: "๊ฐ„์ง€๋‚˜๊ฒŒ ์ˆจ์‰ฌ๊ธฐ",
      done: true,
    },
 .....

Reducers/index.js

export const todoReducer = (state: ITodosArr = initialState, action: TTodoAction) => {
  switch (action.type) {
    case CREATE:
      const createPayload = action.payload as ITodos; // payload ํƒ€์ž…์„ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ
      return Object.assign({}, state, { todos: [...state.todos, createPayload], doing: [...state.doing, createPayload] });

    case REMOVE:
      const removePayload = action.payload as { id: number }; // payload ํƒ€์ž…์„ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ
      const removeTodos = state.todos.filter((el: ITodos) => el.id !== removePayload.id);
     .....
      
    case CHANGE_TEXT:
      const changeTextPayload = action.payload as { id: number; text: string }; // payload ํƒ€์ž…์„ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ
      const changeTodos = state.todos.map((el: ITodos) => {
        if (el.id === changeTextPayload.id) return { ...el, text: changeTextPayload.text };
        return el;
      });
      .....

โ–ท React ์ปดํฌ๋„ŒํŠธ์—์„œ typescript

๐Ÿงค children

React.ReactNode

export interface ITemplateProps {
  children: React.ReactNode;
}

const TodoTemplateNotice = ({ children }: ITemplateProps) => {
  return (
    <TodoTemplateBlock>
      <RealTemplateBlock>{children}</RealTemplateBlock>
    </TodoTemplateBlock>
  );
};

๐Ÿงค MouseEvent

MouseEvent

useEffect(() => {
    const outInput = (e: MouseEvent) => {
      const node = e.target as Node;
      if (isEditable && inputRef.current && !inputRef.current.contains(node)
      .....

๐Ÿงค e.target.value

React.ChangeEvent<HTMLInputElement>

  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setValue(e.target.value);
  };

๐Ÿงค e.preventDefault

React.FormEvent

const onSubmit = (e: React.FormEvent) => {
  e.preventDefault();
}

๐Ÿงค useRef

<HTMLInputElement>

const inputRef = useRef<HTMLInputElement>(null);

๐Ÿงค setState

React.Dispatch<React.SetStateAction<ํƒ€์ž…>>

interface IAdcButtonProps {
  setBtnTxt: React.Dispatch<React.SetStateAction<string>>;
  setAll: React.Dispatch<React.SetStateAction<boolean>>;
  setFilterTxt: React.Dispatch<React.SetStateAction<string>>;
}

const AdcButton = ({ setBtnTxt, setAll, setFilterTxt }: IAdcButtonProps) => {
profile
๐Ÿพ

0๊ฐœ์˜ ๋Œ“๊ธ€