프로젝트 초기화 및 패키지 설치
- 리액트와 타입스크립트를 사용하기에 한번에 다운 받아준다.
# yarn을 사용하는 경우
yarn create react-app my-app --template typescript
# npx를 사용하는 경우
npx create-react-app my-app --template typescript
- 리덕스와 리덕스 관련 라이브러리 설치
yarn add redux react-redux @types/react-redux
- devtools도 사용해주려면 같이 설치 (선택)
yarn add redux-devtools-extension @types/redux-devtools-extension
Create Slice 작성
- 액션 생성자와 리듀서를 자동으로 생성해주는 함수입니다.
// src/store/todoSlice.ts
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
interface TodoState {
items: {
text: string;
completed: boolean;
}[];
}
const initialState: TodoState = {
items: [],
};
const todoSlice = createSlice({
name: 'todo',
initialState,
reducers: {
addTodo: (state, action: PayloadAction<string>) => {
state.items.push({
text: action.payload,
completed: false, // 새로운 todo는 기본값이 false
});
},
removeTodo: (state, action: PayloadAction<number>) => {
state.items.splice(action.payload, 1);
},
toggleTodo: (state, action: PayloadAction<number>) => {
const todo = state.items[action.payload];
todo.completed = !todo.completed; // 완료 상태 변경
},
},
});
export const { addTodo, removeTodo, toggleTodo } = todoSlice.actions;
export default todoSlice.reducer;
리듀서 작성하기
// src/reducers/todoReducer.ts
import { TodoActionTypes, ADD_TODO, TOGGLE_TODO, DELETE_TODO } from '../actions/types';
export interface TodoState {
todos: Todo[];
}
const initialState: TodoState = {
todos: [],
};
const todoReducer = (state = initialState, action: TodoActionTypes): TodoState => {
switch (action.type) {
case ADD_TODO:
return {
...state,
todos: [...state.todos, action.payload],
};
case TOGGLE_TODO:
return {
...state,
todos: state.todos.map((todo) =>
todo.id === action.payload ? { ...todo, completed: !todo.completed } : todo
),
};
case DELETE_TODO:
return {
...state,
todos: state.todos.filter((todo) => todo.id !== action.payload),
};
default:
return state;
}
};
export default todoReducer;
리덕스 사용하기
// src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import store from './store/configStore';
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
스토어 설정하기
- 리덕스 스토어를 설정.
import { configureStore } from '@reduxjs/toolkit';
import todoReducer from './todoSlice';
const store = configureStore({
reducer: {
todo: todoReducer,
},
});
export type RootState = ReturnType<typeof store.getState>;
export default store;
App 컴포넌트
// src/App.tsx
import React, { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { addTodo, removeTodo, toggleTodo } from "./store/todoSlice";
import { RootState } from "./store/configStore";
import styled from "styled-components";
import "./App.css";
const App: React.FC = () => {
const [newTodo, setNewTodo] = useState("");
const todos = useSelector((state: RootState) => state.todo.items);
const dispatch = useDispatch();
const handleAddTodo = () => {
if (newTodo.trim() !== "") {
dispatch(addTodo(newTodo));
setNewTodo("");
}
};
const handleRemoveTodo = (index: number) => {
dispatch(removeTodo(index));
};
const handleToggleTodo = (index: number) => {
dispatch(toggleTodo(index));
};
return (
<AppContainer>
<Title>To-Do List</Title>
<TodoForm>
<input
type="text"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
/>
<button onClick={handleAddTodo}>Add</button>
</TodoForm>
<ul>
{todos.map((todo, index) => (
<TodoItem key={index}>
<TodoText completed={todo.completed}>{todo.text}</TodoText>
<RemoveButton onClick={() => handleRemoveTodo(index)}>
Remove
</RemoveButton>
<CheckboxLabel>
<CheckboxInput
type="checkbox"
checked={todo.completed}
onChange={() => handleToggleTodo(index)}
/>
Completed
</CheckboxLabel>
</TodoItem>
))}
</ul>
</AppContainer>
);
};
export default App;
const AppContainer = styled.div`
font-family: Arial, sans-serif;
max-width: 400px;
margin: 0 auto;
`;
const Title = styled.h1`
text-align: center;
`;
const TodoForm = styled.div`
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 20px;
input {
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
margin-right: 10px;
}
button {
padding: 8px 12px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
&:hover {
background-color: #0056b3;
}
}
`;
const TodoItem = styled.li`
display: flex;
align-items: center;
margin-bottom: 10px;
`;
const TodoText = styled.span<{ completed: boolean }>`
flex: 1;
text-decoration: ${(props) => (props.completed ? "line-through" : "none")};
`;
const RemoveButton = styled.button`
margin-left: 10px;
padding: 4px 8px;
background-color: #dc3545;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
&:hover {
background-color: #c82333;
}
`;
const CheckboxLabel = styled.label`
margin-left: 10px;
cursor: pointer;
`;
const CheckboxInput = styled.input`
margin-right: 5px;
`;
'IT > TypeScript' 카테고리의 다른 글
[TypeScript] 타입 스크립트 기본 (0) | 2023.07.31 |
---|---|
[TypeScript] 타입 스크립트 활용 (0) | 2023.07.26 |
[TypeScript] 타입 스크립트 시작 (0) | 2023.07.26 |