728x90
반응형
지금까지 사용한 상태관리
useState<number>()
useReducer(reducer, state)
2023.02.20 - [typescript] - [typescript]react - typescript(useState)
2023.02.21 - [typescript] - [typescript] react-typscript(usereducer)
react-redux알아보기
2023.01.27 - [react] - [React]redux
이번에 배울건
typescript + redux사용하기!!!!!!!
그 전에~
타입스크립트 지원이 되는지 안되는지 확인
타입스크립트 지원이 안되면 @types/앞에 붙여서 설치
확인방법은
index.d.ts파일이 있는지 확인
라이브러리 2개 설치
redux
react-redux
npm install redux react-redux @types/react-redux
counter모듈 만들기
modules 폴더 생성
counter.ts
//액션타입, 액션생성함수, 리듀서
//1. 액션타입
//action.type 이 string으로 추론되지 않고 'counter/INCREASE' 와 같이
//실제 문자열을 추론되도록 as const 붙임
const INCREASE = "counter/INCREASE" as const;
const DECREASE = "counter/DECREASE" as const;
//2. 액션 생성 함수 // 말그대로 액션생성함수를 리턴해줌 return {type: INCREASE, payload: diff} //액션객체 반환
/* const increase = () => {
return {type: INCREASE}
} */ //생략해서 밑에꺼
export const increase = () => ({type: INCREASE})
/* const decrease = () => {
return {type: DECREASE}
} */ //리턴해주는 얘가 객체일때 소괄호 작성
export const decrease = () => ({type: DECREASE})
//액션객체에 대한 타입 (ReturnType<typeof 메모장 확인)
type CounterAction = ReturnType<typeof increase> | ReturnType<typeof decrease>
//상태에 대한 타입 설정하고 초기상태 설정
type CounterState = {count: number}
//초기상태
const initialState: CounterState = {count: 0}
//3.리듀서 (state,action 타입 지정 해줘야함)
function counter(state:CounterState = initialState, action:CounterAction){
switch(action.type){
case INCREASE:
return {count: state.count + 1};
case DECREASE:
return {count: state.count - 1};
default:
return state;
}
}
export default counter
모듈 합치기
modules폴더 안
index.ts
import {combineReducers} from 'redux';
import counter from './counter';
const rootReducer = combineReducers({counter});
export default rootReducer;
//리듀서 호출 리턴?? 상태!!!
//rootReducer가 실행되면 state리턴함
//ReturnType<typeof rootReducer> 특정함수의 리턴 타입을 추론
//useSelector(state => state.todos)
//스토어의 상태값의 타입을 추론
export type rootState =ReturnType<typeof rootReducer>
//특정함수의 리턴하는 값의 타입을 반환. state의 타입 돌려줌
스토어 생성
src 폴더 안에
index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {legacy_createStore as createStore} from 'redux'
import rootReducer from './modules';
import { Provider } from 'react-redux';
//스토어 생성하기
const store = createStore(rootReducer)
console.log(store.getState())
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
counter 프레젠테이셔널 컴포넌트 생성 (화면구현)
components폴더 생성
Counter.tsx
import React from 'react';
//상태값 count,
//상태 업데이트 해주는 함수 onIncrease, onDecrease
type CounterProps = {
count: number;
onIncrease: ()=> void;
onDecrease: ()=> void;
}
const Counter = ({count, onIncrease, onDecrease}:CounterProps) => {
return (
<div>
<h2>{count}</h2>
<div>
<button onClick={onIncrease}>+</button>
<button onClick={onDecrease}>-</button>
</div>
</div>
);
};
export default Counter;
counter 컨테이너컴포넌트 생성(store에 접근/업데이트)
containers 폴더 생성
CounterContainer.tsx
import React from 'react';
import { useDispatch } from 'react-redux';
import { useSelector } from 'react-redux';
import Counter from '../components/Counter';
import { rootState } from '../modules';
import { decrease, increase } from '../modules/counter';
const CounterContainer = () => {
//상태조회
const count = useSelector((state: rootState)=>state.counter.count)
const dispatch = useDispatch()
//디스패치 함수
const onIncrease =()=>{
dispatch(increase())
}
const onDecrease =()=>{
dispatch(decrease())
}
return (
<div>
<Counter count={count} onDecrease={onDecrease} onIncrease={onIncrease}/>
</div>
);
};
export default CounterContainer;
counterContainer 조립
src 폴더안에
App.tsx
import React from 'react';
import './App.css';
import CounterContainer from './containers/CounterContainer';
function App() {
return (
<div className="App">
<CounterContainer/>
</div>
);
}
export default App;
todolist 만들기
todo 모듈만들기
modules 폴더안
todos.ts
//1.액션타입
//type-action 적용전
const ADD_TODO = "todos/ADD_TODO" as const
const REMOVE_TODO = "todos/REMOVE_TODO" as const
const TOGGLE_TODO = "todos/TOGGLE_TODO" as const
//t새로운 항목 추가할때 사용할 id값
let nextId = 1;
//2.액션 생성 함수
export const addTodo = (text:string) => ({
type: ADD_TODO,
payload: {
id: nextId++,
text: text
}
})
export const toggleTodo = (id:number) => ({
type: TOGGLE_TODO,
payload: id
})
export const removeTodo = (id:number) => ({
type: REMOVE_TODO,
payload: id
})
//ReturnType<typeof addTodo> 특정함수의 리턴타입을 추론
type TodoAction = ReturnType<typeof addTodo>
| ReturnType<typeof removeTodo>
|ReturnType<typeof toggleTodo>
//상태에서 사용할 할일 항목의 타입정의
export type Todo ={
id: number;
text: string;
isDone: boolean;
}
//상태에 대한 타입
export type TodoState = Todo[]
// 초기상태 선언
const initialState: TodoState = [];
//3. 리듀서 함수 - state타입 action타입지정
//type-action 적용전
function todos(state: TodoState= initialState, action:TodoAction){
switch(action.type){
case ADD_TODO:
return [
...state,
{
id: action.payload.id,
text: action.payload.text,
isDone: false
}
]
case TOGGLE_TODO:
//이전상태 배열을 순환하며 배열 요소의 id값과 action의 payload일치하는지
//값이 일치하면 요소의 isDone을 반전해서 리턴
//일치하지 않으면 배열요소 리턴
return state.map(todo=>todo.id === action.payload ?
{...todo, isDone: !todo.isDone}: todo);
case REMOVE_TODO:
return state.filter(todo=> todo.id !== action.payload)
default:
return state
}
}
export default todos;
모듈 합치기
modules폴더 안
index.ts
import {combineReducers} from 'redux';
import counter from './counter';
import todos from './todos';
const rootReducer = combineReducers({counter, todos});
export default rootReducer;
//리듀서 호출 리턴?? 상태!!!
//rootReducer가 실행되면 state리턴함
//ReturnType<typeof rootReducer> 특정함수의 리턴 타입을 추론
//useSelector(state => state.todos)
//스토어의 상태값의 타입을 추론
export type rootState =ReturnType<typeof rootReducer>
//특정함수의 리턴하는 값의 타입을 반환. state의 타입 돌려줌
스토어는 counter만들때 생성함
todoheader부분 프레젠테이셔널 컴포넌트 생성 (화면구현)
components 폴더안
TodoInsert.tsx
import React, {useState} from 'react';
//props 타입 지정 인터페이스
type TodoInsertProps = {
onInsert: (text: string) => void;
}
const TodoInsert = ({onInsert}: TodoInsertProps) => {
//input 입력값 상태관리
const [inputText, setInputText] = useState("")
const onChange = (e:React.ChangeEvent<HTMLInputElement>) => {
setInputText(e.target.value)
}
const onClick = () =>{
onInsert(inputText)
setInputText("")
}
return (
<div>
<input placeholder='할일을 등록하세요' value={inputText} onChange={onChange}/>
<button onClick={onClick}>등록</button>
</div>
);
};
export default TodoInsert;
todolist부분 프레젠테이셔널 컴포넌트 생성 (화면구현)
components 폴더안
TodoLists.tsx
import React from 'react';
import { Todo } from '../modules/todos';
type TodoItemProps = {
todo: Todo;
onToggle: (id:number) => void;
onRemove: (id:number) => void;
}
//스타일 따로 뺄려고 함수 만듬
function TodoItem({todo, onToggle, onRemove}:TodoItemProps) {
const removeStyle: React.CSSProperties = {
textDecoration: todo.isDone? "line-through": "none"
}
const onTodoToggle = () => {
onToggle(todo.id)
}
const onTodoRemove = () => {
onRemove(todo.id)
}
return (
<li style={removeStyle} >
<span onClick={onTodoToggle}>{todo.text}</span>
<button onClick={onTodoRemove}>삭제</button></li>
)
}
type TodoListProps = {
todos: Todo[]
onToggle: (id:number) => void;
onRemove: (id:number) => void;
}
const TodoList = ({todos, onToggle, onRemove}: TodoListProps) => {
return (
<div>
<ul>
{todos.map(todo=> <TodoItem key={todo.id} todo={todo}
onToggle={onToggle} onRemove={onRemove}/>)}
</ul>
</div>
);
};
export default TodoList;
Todolist 컨테이너컴포넌트 생성(store에 접근/업데이트)
containers 폴더 안
TodosContainer.tsx
import React from 'react';
import { useDispatch } from 'react-redux';
import { useSelector } from 'react-redux';
import TodoInsert from '../components/TodoInsert';
import TodoList from '../components/TodoList';
import { rootState } from '../modules';
import { addTodo, removeTodo, toggleTodo } from '../modules/todos';
const TodosContainer = () => {
const todos = useSelector((state: rootState)=>state.todos)
const dispatch = useDispatch();
const onToggle = (id:number) => {
dispatch(toggleTodo(id))
}
const onInsert = (text:string) => {
dispatch(addTodo(text))
}
const onRemove = (id:number) => {
dispatch(removeTodo(id))
}
return (
<div>
<TodoInsert onInsert={onInsert} />
<TodoList todos={todos} onToggle={onToggle} onRemove={onRemove}/>
</div>
);
};
export default TodosContainer;
TodoContainer 조립
src 폴더 안
App.tsx
import React from 'react';
import logo from './logo.svg';
import './App.css';
import CounterContainer from './containers/CounterContainer';
import TodosContainer from './containers/TodosContainer';
function App() {
return (
<div className="App">
<CounterContainer/>
<TodosContainer/>
</div>
);
}
export default App;
728x90
반응형
'typescript' 카테고리의 다른 글
[typescript] json-server라이브러리, react-typescript(styled-components) (2) | 2023.02.24 |
---|---|
[typescript] typesafe-actions 라이브러리 (1) | 2023.02.24 |
[typescript] as & ReturnType (0) | 2023.02.24 |
[typescript] react-typscript(useReducer + contextAPI) (0) | 2023.02.24 |
[typescript] react-typscript(usereducer) (0) | 2023.02.24 |
댓글