Stack/TypeScript

[TS / React] typesafe-actions을 이용한 refactoring

7ingout 2022. 7. 29. 15:38

https://www.npmjs.com/package/typesafe-actions

 

typesafe-actions

Typesafe Action Creators for Redux / Flux Architectures (in TypeScript). Latest version: 5.1.0, last published: 3 years ago. Start using typesafe-actions in your project by running `npm i typesafe-actions`. There are 402 other projects in the npm registry

www.npmjs.com

 

https://github.com/piotrwitek/typesafe-actions/issues/143

 

v5.0.0 - Rewrite and simplify `createAction` API · Issue #143 · piotrwitek/typesafe-actions

Is your feature request related to a real problem or use-case? Currently, createAction API is not optimal and require some unnecessary boilerplate code (historically it was created pre TS v3.0 so t...

github.com

createStandardAction 5버전 나옴

 

TS-REDUX-TUTORIAL

npm install typesafe-actions

 

2022.07.28 - [Coding/TypeScript] - [TS / React] Redux로 Counter 구현

 

[TS / React] Redux로 Counter 구현

react-redux 1. 모듈리듀서 1) 액션타입 지정 2) 액션생성 함수 3) 초기값 4) 리듀서 함수 작성 -> 루트리듀서 2. 스토어 생성 const store = createStore(루트리듀서) 3.컴포넌트 생성 1) 컨테이너 컴포넌트 2)..

7ingout.tistory.com

 

modules/counter.ts

// 1. 액션타입
// 2. 액션을 리턴해주는 함수
// 3. 초기값
// 4. 리듀서 만들기

// import { createStandardAction } from "typesafe-actions/dist/deprecated/create-standard-action";
import { ActionType, createReducer, deprecated } from "typesafe-actions";
const { createStandardAction } = deprecated;

// 액션타입
// export const INCREASE = "counter/INCREASE" as const;
// export const DECREASE = "counter/DECREASE" as const;
// export const INCREASE_BY = "counter/INCREASE_BY" as const;

// typesafe-actions 설치후
export const INCREASE = "counter/INCREASE";
export const DECREASE = "counter/DECREASE";
export const INCREASE_BY = "counter/INCREASE_BY";


// 액션 생성함수
// typesafe-actions 설치후 주석처리함
// export const increase = () => ({ type: INCREASE })
// export const decrease = () => ({ type: DECREASE })
// export const increaseBy = (diff: number) => ({ 
//     type: INCREASE_BY,
//     payload: diff
// })
export const increase = createStandardAction(INCREASE)();
export const decrease = createStandardAction(DECREASE)();
export const increaseBy = createStandardAction(INCREASE_BY)<number>();

// 스테이트의 타입을 지정
type CounterState = {
    count: number;
}

// 초기상태 생성
const initialState = {
    count: 0
}

// 리듀서 액션 타입지정
// ReturnType<typeof ---> 특정함수의 반환값을 추론

// 리듀서 액션 타입지정
// typesafe-actions 설치후 주석처리함
// type CounterAction = 
// | ReturnType<typeof increase>
// | ReturnType<typeof decrease>
// | ReturnType<typeof increaseBy>
const actions = {increase, decrease, increaseBy}
type CounterAction = ActionType<typeof actions>

// 리듀서 만들기
// typesafe-actions 설치후 주석처리함
// export default function counter(state: CounterState = initialState, action: CounterAction) : CounterState{
//     switch(action.type) {
//         case 'counter/INCREASE':
//             return { count: state.count + 1 };
//         case 'counter/DECREASE':
//             return { count: state.count - 1 };
//         case 'counter/INCREASE_BY':
//             return { count: state.count + action.payload };
//         default:
//             return state;
//     }
// }
// createRecuer을 통해서 오브젝트맵 형태로 리듀서를 구현
const counter = createReducer<CounterState, CounterAction>(initialState, {
    [INCREASE]: state => ({ count: state.count + 1 }),
    [DECREASE]: state => ({ count: state.count -1 }),
    [INCREASE_BY]: (state, action) => ({ count: state.count + action.payload})
})
export default counter;

 


 

2022.07.29 - [Coding/TypeScript] - [TS / React] Redux로 To-do List 구현

 

[TS / React] Redux로 To-do List 구현

2022.07.28 - [Coding/TypeScript] - [TS / React] Redux로 Counter 구현 루트리듀서 2. 스토어 생성 const store = createStore(루트리듀서) 3.컴포넌트 생성 1) 컨테이너 컴포넌트 2).." data-og-host="7ingout...

7ingout.tistory.com

 

modules/todos_sample.ts

// actions.ts/ index.ts/ reducer.ts/ types.ts

import { ActionType, createReducer, deprecated } from "typesafe-actions";
const { createStandardAction } = deprecated;
// 액션타입 선언, 액션 생성 함수, 초기값, 리듀서
// 할 일 추가, 할 일 제거, 할 일 체크

// 액션타입 선언
// typesafe-actions 설치후
// const ADD_TODO = 'todos/ADD_TODO' as const;
// const TOGGLE_TODO = 'todos/TOGGLE_TODO' as const;
// const REMOVE_TODO = 'todos/REMOVE_TODO' as const;
const ADD_TODO = 'todos/ADD_TODO';
const TOGGLE_TODO = 'todos/TOGGLE_TODO';
const REMOVE_TODO = 'todos/REMOVE_TODO';

let nextId = 1;
// 액션 생성 함수
export const addTodo: any = (text: string) => ({
    type: ADD_TODO,
    payload: {
        id: nextId++,
        text
    }
})
// export const toggleTodo = (id:number) => ({
//     type: TOGGLE_TODO,
//     payload: id
// })
// export const removeTodo = (id:number) => ({
//     type: REMOVE_TODO,
//     payload: id
// })
export const toggleTodo = createStandardAction(TOGGLE_TODO)<number>();
export const removeTodo = createStandardAction(REMOVE_TODO)<number>();


// 액션 객체들에 대한 타입 작성
// type TodosAction = 
// | ReturnType<typeof addTodo>
// | ReturnType<typeof toggleTodo>
// | ReturnType<typeof removeTodo>
const actions = ({
    addTodo,
    toggleTodo,
    removeTodo
})
// typesafe-actions 설치후
// 모든 액션 객체들에 대한 타입준비
// 함수의 리턴타입을 추론
type TodosAction = ActionType<typeof actions>

// 상태에서 사용할 할일 항목 데이터 정리
export type Todo = {
    id: number;
    text: string;
    done: boolean;
}

// 이 모듈에서 관리할 상태 타입 작성
export type TodoState = Todo[]

// 초기상태 선언
const initialState: TodoState = [];

// 리듀서 작성하기
// function todos(
//     state: TodoState = initialState,
//     action: TodosAction
// ) : TodoState {
//     switch(action.type) {
//         case ADD_TODO:
//             return state.concat({
//                 id:action.payload.id,
//                 text: action.payload.text,
//                 done: false
//             });
//         case TOGGLE_TODO:
//             return state.map(todo=>
//                 todo.id === action.payload ? {...todo, done: !todo.done } : todo
//                 )
//         case REMOVE_TODO:
//             return state.filter(todo => todo.id !== action.payload);
//         default:
//             return state
//     }
// }
const todos_sample = createReducer<TodoState, TodosAction>(initialState, {
    [ADD_TODO]: (state, action) => {
        return state.concat({
            ...action.payload,
            done: false
        })
    },
    [TOGGLE_TODO]: (state, { payload: id }) =>
    state.map(todo => (todo.id === id ? {...todo, done: ! todo.done} : todo )),
    [REMOVE_TODO]: (state, { payload: id }) =>
    state.filter(todo => todo.id !== id)
})

export default todos_sample;

 

// modules/todos_sample.ts파일을 쪼개기

modules/todos/actions.ts 

import { ActionType, createReducer, deprecated } from "typesafe-actions";
const { createStandardAction } = deprecated;

// 액션타입 선언
export const ADD_TODO = 'todos/ADD_TODO';
export const TOGGLE_TODO = 'todos/TOGGLE_TODO';
export const REMOVE_TODO = 'todos/REMOVE_TODO';

let nextId = 1;
// 액션 생성 함수
export const addTodo: any = (text: string) => ({
    type: ADD_TODO,
    payload: {
        id: nextId++,
        text
    }
})
export const toggleTodo = createStandardAction(TOGGLE_TODO)<number>();
export const removeTodo = createStandardAction(REMOVE_TODO)<number>();

 

modules/todos/types.ts

import { ActionType } from "typesafe-actions";
import * as actions from "./actions";

// 모든 액션 객체들에 대한 타입준비
// 함수의 리턴타입을 추론
export type TodosAction = ActionType<typeof actions>

// 상태에서 사용할 할일 항목 데이터 정리
export type Todo = {
    id: number;
    text: string;
    done: boolean;
}

// 이 모듈에서 관리할 상태 타입 작성
export type TodoState = Todo[]

 

modules/todos/reducer.ts

import { TodoState, TodosAction } from "./types";
import { createReducer } from "typesafe-actions";
import { ADD_TODO, TOGGLE_TODO, REMOVE_TODO } from './actions'

// 초기상태 선언
const initialState: TodoState = [];

// 리듀서 작성하기
const todos = createReducer<TodoState, TodosAction>(initialState, {
    [ADD_TODO]: (state, action) => {
        return state.concat({
            ...action.payload,
            done: false
        })
    },
    [TOGGLE_TODO]: (state, { payload: id }) =>
    state.map(todo => (todo.id === id ? {...todo, done: ! todo.done} : todo )),
    [REMOVE_TODO]: (state, { payload: id }) =>
    state.filter(todo => todo.id !== id)
})

export default todos;

 

modules/todos/index.ts

export { default } from './reducer';
export * from './actions';
export * from './types';

//