-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Configure TypeScript (DL-17) #53
base: develop
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
Button example: | ||
|
||
```js | ||
<Button text="Button" /> | ||
<Button>Button Example</Button> | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import React from 'react'; | ||
import { Props } from './Button.types'; | ||
|
||
// TODO 버튼 컴포넌트 작성 | ||
const Button: React.FC<Props> = ({ children, type = 'button', ...rest }) => ( | ||
<button {...rest} type={type}> | ||
{children} | ||
</button> | ||
); | ||
|
||
export default Button; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
type AttributeProps = React.ButtonHTMLAttributes<HTMLButtonElement>; | ||
|
||
type CustomProps = { | ||
theme?: 'primary' | 'secondary'; | ||
}; | ||
|
||
export type Props = AttributeProps & CustomProps; | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export interface UserSampleState { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 인터페이스 정의할 때는 앞에 I 붙여 주는게 좋아욤 ㅎㅎ tslint 로 자동으로 체크해 줄 수도 있구요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 부분은 naming convention이라 정하기 나름일것 같은데 |
||
id: number; | ||
email: string; | ||
first_name: string; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 필드이름은 camel case? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이부분 제가 잘 몰라서 그러는데 데이터가 |
||
last_name: string; | ||
avatar: string; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,22 +2,26 @@ import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'; | |
import rootReducer from './store'; | ||
import loggerMiddleware from './middlewares/logger'; | ||
import monitorReducerEnhancer from './enhancers/monitorReducers'; | ||
import { AppState } from './store/store.types'; | ||
|
||
export default function configureAppStore(preloadedState) { | ||
export type AppDispatch = ReturnType<typeof configureAppStore>['dispatch']; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
const configureAppStore = (preloadedState = {}) => { | ||
const store = configureStore({ | ||
reducer: rootReducer, | ||
preloadedState, | ||
middleware: [loggerMiddleware, ...getDefaultMiddleware()], | ||
middleware: [loggerMiddleware, ...getDefaultMiddleware<AppState>()], | ||
enhancers: [monitorReducerEnhancer], | ||
}); | ||
|
||
if (process.env.NODE_ENV === 'development' && module.hot) { | ||
module.hot.accept('./store', () => { | ||
// eslint-disable-next-line global-require | ||
const newRootReducer = require('./store').default; | ||
store.replaceReducer(newRootReducer); | ||
}); | ||
} | ||
|
||
return store; | ||
} | ||
}; | ||
|
||
export default configureAppStore; |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import React from 'react'; | ||
|
||
import './Home.css'; | ||
|
||
function Home() { | ||
return ( | ||
<div className="home"> | ||
<header className="home__header"> | ||
<h1>d.log</h1> | ||
</header> | ||
</div> | ||
); | ||
} | ||
|
||
export default Home; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/// <reference types="react-scripts" /> |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import axios from 'axios'; | ||
import { createAsyncThunk } from '@reduxjs/toolkit'; | ||
import { createStandardAction } from '../action-helpers'; | ||
import { UserSampleState } from './ReduxSample.types'; | ||
import { AppState } from '../store.types'; | ||
import { AppDispatch } from '../../configureStore'; | ||
|
||
interface ThunkAPI { | ||
state: AppState; | ||
dispatch: AppDispatch; | ||
} | ||
|
||
export const GET_SAMPLE_USERS = '@sample/GET_SAMPLE_USERS'; | ||
export const TRIGGER_BTN_CLICK = '@sample/TRIGGER_BTN_CLICK'; | ||
export const RESET = '@sample/RESET'; | ||
|
||
const fetchSampleUsers = async () => { | ||
try { | ||
const { data } = await axios.get('https://reqres.in/api/users'); | ||
return data.data; | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
}; | ||
|
||
const ReduxSampleActions = { | ||
getSampleUsers: createAsyncThunk<UserSampleState[], void, ThunkAPI>(GET_SAMPLE_USERS, fetchSampleUsers), | ||
triggerBtnClick: createStandardAction<{ message: string }>(TRIGGER_BTN_CLICK), | ||
reset: createStandardAction(RESET), | ||
}; | ||
|
||
export default ReduxSampleActions; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,30 @@ | ||
import { createReducer } from '@reduxjs/toolkit'; | ||
import ReduxSampleActions from './ReduxSample.actions'; | ||
import ReduxSampleActions, { GET_SAMPLE_USERS, TRIGGER_BTN_CLICK, RESET } from './ReduxSample.actions'; | ||
import { ReduxSampleState } from './ReduxSample.types'; | ||
|
||
export const initialState = { | ||
export const initialState: ReduxSampleState = { | ||
sampleUsers: [], | ||
sampleButton: { | ||
message: 'Fetch Users', | ||
isClicked: false, | ||
loading: false, | ||
}, | ||
}; | ||
|
||
// TODO createReducer 타입 추가 | ||
const reduxSampleReducer = createReducer(initialState, { | ||
// TODO createAsyncAction helper 생성 후 fulfilled 상태 타입 확인 | ||
[ReduxSampleActions.getSampleUsers.fulfilled]: (state, { payload: userData }) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아하 type을 넣으면 됐었네요! ㅎㅎ |
||
console.log(userData); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. console.log 빼주세욤 :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 앗 넵ㅎㅎ |
||
state.sampleUsers = userData; | ||
}, | ||
[ReduxSampleActions.triggerBtnClick]: (state, action) => { | ||
[TRIGGER_BTN_CLICK]: (state, action) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
이렇게 써도 될 것 같아욤ㅎㅎ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵~! |
||
const { message } = action.payload; | ||
|
||
state.sampleButton.message = message; | ||
state.sampleButton.loading = true; | ||
}, | ||
[ReduxSampleActions.reset]: () => initialState, | ||
[RESET]: () => initialState, | ||
}); | ||
|
||
export default reduxSampleReducer; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
export interface UserSampleState { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
id: number; | ||
email: string; | ||
first_name: string; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기도 camel case :)) |
||
last_name: string; | ||
avatar: string; | ||
} | ||
|
||
export interface ReduxSampleState { | ||
sampleUsers: UserSampleState[]; | ||
sampleButton: { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ISampleButton 도 따로 정의해주세욤
|
||
message: string; | ||
isClicked: boolean; | ||
loading: boolean; | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { createAction } from '@reduxjs/toolkit'; | ||
|
||
function actionWithPayload<T>() { | ||
return (payload?: T) => ({ payload }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
이렇게 처리했는데 말씀하시는게 맞는지 모르겠네용 ㅎㅎ |
||
} | ||
|
||
export const createStandardAction = <T>(action: string) => createAction(action, actionWithPayload<T>()); | ||
|
||
// TODO createStandardAction와 같이 createThunkAction 용으로 createAsyncAction helper 생성 필요 (thunkApi를 이용하려면 generic을 받아야함) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import rootReducer from './index'; | ||
|
||
export type AppState = ReturnType<typeof rootReducer>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TAppState 라고 명시해주는건 어떨까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 만약 T자로 시작해야하면 곤란하기도 하고 그렇게되면 가독성이 더 떨어질 우려가 있어 naming 앞에 prefix 붙이는건 가급적이면 안했으면 합니다. (이름 짓는게 제일 어려운것 같아요 ㅠㅠ). Redux에서 쓰는 state들의 interface 명들 엔딩으로 |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import React from 'react'; | ||
import classNames from 'classnames'; | ||
import './StyleContainer.scss'; | ||
import { DirectionType, PosXType, PosYType } from '../../types/ui.types'; | ||
|
||
type AttributeProps = React.HTMLAttributes<HTMLDivElement>; | ||
|
||
type Props = AttributeProps & { | ||
direction: DirectionType; | ||
posX: PosXType; | ||
posY: PosYType; | ||
}; | ||
|
||
const StyleContainer: React.FC<Props> = ({ direction = 'horizontal', posX = 'left', posY = 'top', ...rest }) => ( | ||
<div | ||
{...rest} | ||
className={classNames('style-container', `position-${posX}-${posY}`, { | ||
column: direction === 'horizontal', | ||
})} | ||
/> | ||
); | ||
|
||
export default StyleContainer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interface 대신 type 키워드로 컴포넌트 prop 스펙을 정의해도 상관없긴 한데,
확장성 같은 몇가지 기능 차이가 있어욤 ㅎㅎ
차후 다른 형태의 버튼 인터페이스가 필요하다면
기존 인터페이스를 extends 해서 확장 가능해요 type 은 안되구요ㅠㅠ
그래서 아래와 같이 쓰는건 어떨까요 ? :))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아래 문서 보고 정한것인데 컴포넌트 내에서 사용하는 Props나 State는 type으로 정의하고 API 관련된 타입들은 interface를 사용하고자 합니다. 타입스크립트에서도 권장하는 ruleset에서 interface-over-type-literal을 제거 했더라구요.