React Hooks + Redux+TypeScript
React kullanırken state managment için her ne kadar contextler üzerinden statelerimizi yönetebiliyor olsak da hali hazırda olan çeşitli state managment kütüphaneleri (redux,mobx) gibi hala kullanılmaktadır. Bu yazımızda react hooklarla beraber redux’ı nası kullanacağımızı göreceğiz bunu yaparken de typescript’den yararlanıyor olacağız. Örneğimizde basitçe bir todo modulü barındırıyor olacağız. Lafı uzatmadan başlayalım.
Kurulumlar
1-)React projesi oluşturma
npx create-react-app my-app --template typescript
2-)redux , react-redux , axios , redux-thunk kurulumu
npm install --save react-redux @types/react-redux
npm install --save axios
npm install --save redux-thunk
npm install --save redux
Redux dosya ve klasör yapısının kurulması
src/store ve src/store/todo
klasörlerini oluşturalım.
Todo Modulü:
src/store/todo/todoModel.ts
dosyamızı
export interface todoModel{userId:number,id:numbertitle:stringcompleted:boolean}
src/store/todo/state.ts
dosyamızı oluşturalım.
export interface todoState {error: stringloading: booleantodos: Array<todoModel>}export const initialState: todoState = {loading: false,todos: [],error: ''}
todo modülümüz için ilgili state değerimizi ve interfacemizi oluşturduk. Statemizi açıklayacak olursak;
error: Oluşan hatalar için tutacağımız değer
loading: Veri çekerken verilerin yükleniyor olduğunu göstermek için kullanacağımız değer
todos: İçerisinde verilerimizi barındıracağımız değer.
src/store/todo/const.ts
reducerlar da kullanacağımız action typelarımızı oluşturalım.
export const FETCH_TODO_REQUEST="FETCH_TODO_REQUEST"
export const FETCH_TODO_SUCCESS="FETCH_TODO_SUCCESS"
export const FETCH_TODO_ERROR="FETCH_TODO_ERROR"
export const DELETE_TODO_SUCCESS="DELETE_TODO_SUCCESS"
export const DELETE_TODO_ERROR="DELETE_TODO_ERROR"
src/store/todo/actionsModels.ts
import {DELETE_TODO_ERROR,DELETE_TODO_REQUEST,DELETE_TODO_SUCCESS,FETCH_TODO_ERROR,FETCH_TODO_REQUEST,FETCH_TODO_SUCCESS
import { todoModel } from "./todoModel"} from "./consts";export interface todosFetch {type: typeof FETCH_TODO_REQUEST}export interface todoSuccess {type: typeof FETCH_TODO_SUCCESSpayload: Array<todoModel>}export interface todoError {type: typeof FETCH_TODO_ERRORerror: any}export interface deleteTodoRequest {type: typeof DELETE_TODO_REQUEST}export interface deleteTodoSuccess {type: typeof DELETE_TODO_SUCCESSpayload: any}export interface deleteTodoError {type: typeof DELETE_TODO_ERRORerror: any}export type todoActions =todosFetch| todoSuccess| todoError| deleteTodoRequest| deleteTodoError| deleteTodoSuccess;
actionlarımızla ilgili interfacelerimizi oluşturduğumuza göre şimdi actionslarımızı yazmaya başlayabiliriz.
src/store/todo/actions.ts
import {DELETE_TODO_ERROR,DELETE_TODO_SUCCESS,FETCH_TODO_ERROR,FETCH_TODO_REQUEST,FETCH_TODO_SUCCESS} from "./consts";import axios from 'axios'import {deleteTodoError,deleteTodoSuccess, todoError,todoSuccess,todosFetch} from './actionsModels'import { todoModel } from "./todoModel";export const deleteTodoErrorRequest = (error: any): deleteTodoError => ({error,type: DELETE_TODO_ERROR});export const delTodoSuccess = (payload: any): deleteTodoSuccess => ({type: DELETE_TODO_SUCCESS,payload})export const todoFetch = (): todosFetch => ({type: FETCH_TODO_REQUEST,});export const todoFetchSuccess = (payload: Array<todoModel>): todoSuccess => ({type: FETCH_TODO_SUCCESS,payload});export const todoFetchError = (error: any): todoError => ({type: FETCH_TODO_ERROR,error});export const deleteTodos = (item: todoModel) => {return async (dispatch: any) => {dispatch(delTodoSuccess(item))}}export const fetchTodos = () => {return async (dispatch: any) => {dispatch(todoFetch());try {const response = await axios.get('https://jsonplaceholder.typicode.com/todos')dispatch(todoFetchSuccess(response.data))} catch (error) {dispatch(todoFetchError(error))}}}
ilgili actionslarımızın tiplerini belirterek oluşturduk, yalnızca fetch işlemi için backend bağlantısı kullanıyorum.
src/store/todo/reducers.ts
import {initialState} from "./state";import {todoActions} from "./actionsModels";import {DELETE_TODO_ERROR,DELETE_TODO_SUCCESS,FETCH_TODO_ERROR,FETCH_TODO_REQUEST,FETCH_TODO_SUCCESS} from "./consts";const todoReducers = (state = initialState, action: todoActions) => {switch (action.type) {case FETCH_TODO_REQUEST:return {...state,loading: true}case FETCH_TODO_ERROR:return {...state,todos: [],error: action.error}case FETCH_TODO_SUCCESS : {return {...state,todos: action.payload,loading: false}}case DELETE_TODO_ERROR: {return {...state,error: action.error}}case DELETE_TODO_SUCCESS: {let newTodos = [...state.todos]newTodos = newTodos.filter((item) => item.id !== action.payload.id)return {...state,loading: false,todos: newTodos}}default:return state}};export default todoReducers
todo modülü için gerekli reducerı da oluşturduktan sonra bu modül ile işimizi tamamlayıp bir sonraki adıma geçiyoruz.
src/store/todo/rootState.ts
import {todoState} from "./todos/state";export interface rootState {todo: todoState}
kullanacağımız modüle ait interface oluşturduktan sonra sırada rootReducerımızı oluşturalım.
src/store/todo/rootReducers.ts
import {combineReducers} from "redux";import todoReducers from "./todos/reducers";const rootReducer = combineReducers({todo: todoReducers})export default rootReducer
rootReducersımızda tek bir modul olmasına rağmen ilerisini ve farklı modüller olacağını düşünerek combineReducers fonskiyonunu kullanıyorum ardından src/store/todo/store.ts
import {createStore, applyMiddleware} from 'redux'import rootReducer from "./rootReducers";import thunk from "redux-thunk";const store = createStore(rootReducer, applyMiddleware(thunk))export default store;
store dosyamızda ilgili tanımlamaları ve middleware gerçekleştirip en nihayetinde src/index.tsx
dosyamızda Provider ile bütün uygulamayı kaplayıp ilgili store dahil ediyoruz.
import React from 'react';import ReactDOM from 'react-dom';import './index.css';import App from './App';import reportWebVitals from './reportWebVitals';import {Provider} from 'react-redux'import store from "./store/store";ReactDOM.render(<React.StrictMode><Provider store={store}><App/></Provider></React.StrictMode>,document.getElementById('root'));
Redux yapımızı kullanalım
src/app.tsx
dosyamızda
import {useDispatch, useSelector} from "react-redux";import {fetchTodos} from "./store/todos";import {rootState} from "./store/rootState";import React, {useLayoutEffect}function App() {const state = useSelector<rootState, rootState['todo']>(state => state.todo)const dispatch = useDispatch();useLayoutEffect(() => {getTodo();}, [])const getTodo = () => {dispatch(fetchTodos())}
return (<div className="App"><pre>{JSON.stringify(state, null,2)}</pre></header></div>);
}
useSelector hookunu kullanarak state içersinde yer alan todo modülünü kullanacağımı belirttim ve component ilk render olduğunda fetchTodos’u dispatch ettim.
Herkese İyi Çalışmalar Dilerim. :)