안녕하세요. 후르륵짭짭입니다.
취미 생활로 하는 Expo에 Redux ToolKit을 적용한 내용을 작성하도록 하겠습니다.
https://redux-toolkit.js.org/usage/usage-with-typescript
이 글을 보고 Redux ToolKit을 Typescript로 적용하는 방법을 공부를 해보려고 합니다.
** 설치 **
npm install @reduxjs/toolkit react-redux redux-thunk
** CreateSlice **
실질적으로 행동을 담당하는 역할을 한다.
createSlice는 Redux Toolkit에서 리듀서와 관련된 로직을 간단하게 작성할 수 있도록 도와주는 함수다.
import { createSlice, PayloadAction } from "@reduxjs/toolkit"
import { fetchInfos } from "./thunks";
interface ContentsStateType { // 슬라이스 초기 상태 타입
count: number
}
const initialState: ContentsStateType = {
count: 0
};
const counterReducer = createSlice({
name: 'counter',
initialState: initialState,
reducers: {
addCount(state,actions: PayloadAction<number>) {
console.log(actions.payload)
state.count + actions.payload
}
},
extraReducers: (builder) => {
builder.addCase(fetchInfos.fulfilled, (state, { payload }) => {
console.log("success extra Reducer",payload)
state.count + 10
}),
builder.addCase(fetchInfos.rejected, (state, { payload }) => {
console.log("reject extra Reducer",payload)
state.count = state.count - 10
})
}
})
export const { addCount } = counterReducer.actions
export default counterReducer
- Reducer -
Reducer에는 State와 Action이 존재한다.
State는 값이 변경이 되면 Provider 하위의 View를 rendering 시켜준다.
Action에는 Payload라는 것이 존재하는데, 이것은 Input 값이라 생각하면 된다.
- ExtraReducer -
//HTTP.tsx
import axios from "axios";
let baseUrl: string = "https://testproject2-2e6ef-default-rtdb.firebaseio.com/"
export interface GetInfoResponse {
userName: string
}
export const getInfos = async (): Promise<GetInfoResponse> => {
let response = await axios.get<GetInfoResponse>(baseUrl + "infos.json")
return response.data
}
//thunks.tsx
import { createAsyncThunk } from "@reduxjs/toolkit"
import { GetInfoResponse } from "../utilitys/http"
import { RootState } from "./store"
import { getInfos } from "../utilitys/http"
export const fetchInfos = createAsyncThunk<GetInfoResponse,void,{ state: RootState, rejectValue: { error: string } }>(
'counter/getInfos',
async (_, { getState , rejectWithValue }) => {
const state = getState().counter
console.log("state in FetchInfos", state)
if (state.count % 2 !== 0 ) {
console.log("state Count is Not 2")
return rejectWithValue({error: "Fail"})
}
const response = await getInfos()
return response
}
)
CreateAsyncThunk라는 것이 존재한다.
이것은 비동기 작업에 대한 Reducer를 만들 때 사용한다.
CreateAsyncThunk<Response Type, Input Type, 각종 옵션>( 이름 , 비동기함수)
이렇게 작성해줘야한다.
각종 옵션은 여러가지가 있는데, state의 타입과 에러일 경우에 반환하는 rejectValue 타입을 주도록 만들었다.
extraReducers: (builder) => {
builder.addCase(fetchInfos.fulfilled, (state, { payload }) => {
console.log("success extra Reducer",payload)
state.count + 10
}),
builder.addCase(fetchInfos.rejected, (state, { payload }) => {
console.log("reject extra Reducer",payload)
state.count = state.count - 10
})
}
그리고 이렇게 extraReducer에서 state에 대한 옵션을 처리하고 있다.
fulfilled는 성공했을 때, reject는 실패 했을 때, 다음 함수를 타게 된다.
이때, payload에는 반환되는 Input이 들어온다.
** Store **
이렇게 행동에 대한 함수들을 만들었으니, 행동을 사용할 수 있도록 만들어줘야한다.
import { configureStore, combineReducers } from "@reduxjs/toolkit"
import counterReducer from "./counterReducer"
import { useSelector, useDispatch , TypedUseSelectorHook} from 'react-redux';
const rootReducer = combineReducers({
counter: counterReducer.reducer
// Add more slices as needed
});
const store = configureStore({
reducer: rootReducer
})
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
export default store
이렇게 만들었던 reducer를 combineReducers에 넣어준다.
여러가지 Reducer를 넣을 수 있다.
그리고 configureStore를 통해서 reducer를 넣어준다.
https://velog.io/@jjh099/Redux-configureStore-createSlice-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0
자세한 내용은 여기에 있다.
- 사용하기 -
import { useSelector, useDispatch , TypedUseSelectorHook} from 'react-redux';
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
외부에서 TypeScript형식으로 사용하기 위해서는
Dispatch와 Selector를 타입을 지정하여 새롭게 정의 해줘야한다.
** 실제 앱 적용 **
import { Provider } from 'react-redux';
import store from './store/store';
import CounterScreen from './CounterScreen';
export default function App() {
return (
<Provider store={store}>
<View style={styles.container}>
<CounterScreen />
<StatusBar style="auto" />
</View>
</Provider>
);
}
이렇게 내가 사용하려고 하는 View에
<Provider> 컴포넌트로 감싸준다. 이때, State를 감지할 Store를 넣어준다.
import { addCount } from './store/counterReducer';
import { RootState } from './store/store';
import { useAppSelector, useAppDispatch } from './store/store';
import { fetchInfos } from './store/thunks';
export default function CounterScreen() {
const counterState = useAppSelector( (state: RootState) => state.counter)
const dispatch = useAppDispatch()
const countUp = (_: any) => {
dispatch(addCount(1))
}
const getInfo = (_: any) => {
dispatch(fetchInfos())
}
return (
<View style={styles.info}>
<Text>Count Of App : {counterState.count}</Text>
<Pressable onPress={countUp}>
<Text>Count Up</Text>
</Pressable>
<Pressable onPress={getInfo}>
<Text>getInfo</Text>
</Pressable>
</View>
);
}
그리고 이렇게 useAppSelector로 State를 가져올 수 있고
dispatch를 통해서 함수 호출을 해줄 수 있다.
참고사이트:
- basic
https://medium.com/@Has_San/difference-between-redux-and-redux-toolkit-7e1e5431546d
https://dev.to/emmyjaff/creating-a-react-native-expo-project-with-redux-toolkit-and-thunk-73k
- AsyncThunk
https://velog.io/@raejoonee/createAsyncThunk
- 고급 TypeScript + Redux
https://velog.io/@velopert/use-typescript-and-redux-like-a-pro
- Action Type지정
https://stackoverflow.com/questions/69834242/redux-toolkit-typescript-what-is-my-payloadaction-type
https://velog.io/@ajm0718/Vite-TS-React%EC%97%90-Redux-Toolkit-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0
https://velog.io/@jjh099/Redux-configureStore-createSlice-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0
- TypeScript 기본
https://velog.io/@jinyoung985/TypeScript-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC
'React > Native' 카테고리의 다른 글
EXPO) EXPO에 앱 업로드 하기 (Simulator & Manual Build) (0) | 2024.12.06 |
---|---|
ReactNative) ScrollView와 FlatList의 비교 (feat: React의 &&와 ...) (0) | 2023.03.08 |
ReactNative) ReactNative 취미 시작 (feat: Component, FlexBox) (0) | 2023.03.01 |
댓글