๐ŸŽ Redux + React + TypeScript

29380 ๋‹จ์–ด reduxtypescriptReactReact

๐Ÿ“ƒ index.tsx

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {Provider} from 'react-redux';
import store from './store';

ReactDOM.render(
1๏ธโƒฃ <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

๐Ÿ“‹ Memo(index.tsx)

    1๏ธโƒฃ <React.StrictMode>
      1. ์•ˆ์ „ํ•˜์ง€ ์•Š์€ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฐพ๋Š”๋‹ค.
      2. ๋ ˆ๊ฑฐ์‹œ ๋ฌธ์ž์—ด ref ์‚ฌ์šฉ์— ๋Œ€ํ•œ ๊ฒฝ๊ณ ํ•œ๋‹ค.
      3. ๊ถŒ์žฅ๋˜์ง€ ์•Š๋Š” findDOMNode ์‚ฌ์šฉ์— ๋Œ€ํ•œ ๊ฒฝ๊ณ ํ•œ๋‹ค.
      4. ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ถ€์ž‘์šฉ์„ ๊ฒ€์‚ฌํ•œ๋‹ค.
      5. ๋ ˆ๊ฑฐ์‹œ context API๋ฅผ ๊ฒ€์‚ฌํ•œ๋‹ค.

๐Ÿ“ƒ store.ts

import {applyMiddleware, createStore} from 'redux';
import rootReducer from './reducers/index'
1๏ธโƒฃ import thunk from 'redux-thunk'

const store = createStore(rootReducer, applyMiddleware(thunk));

export type RootReducerType = 2๏ธโƒฃ ReturnType<typeof rootReducer>

export default store;

๐Ÿ“‹ Memo(store.ts)

    1๏ธโƒฃ import thunk from 'redux-thunk'
      1.๋น„๋™๊ธฐ๋กœ redux state๋“ค์„ ๊ด€๋ฆฌ ํ•  ๋•Œ ํ•„์š”ํ•œ middleware์ด๋‹ค.
      
    2๏ธโƒฃ ReturnType<typeof rootReducer>
      1. ReturnType ํƒ€์ž…์€ ์ฃผ์–ด์ง„ generic type์˜ return type์„ ํ• ๋‹นํ•œ๋‹ค.

๐Ÿ“ reducers

๐Ÿ“ƒ index.ts

1๏ธโƒฃ import {combineReducers} from 'redux';
import PokemonReducer from './PokemonReducer';

const rootReducer = combineReducers({
    PokemonReducer
})

export default rootReducer;

๐Ÿ“‹ Memo(index.ts)

    1๏ธโƒฃ import {combineReducers} from 'redux';
      1.์—ฌ๋Ÿฌ๊ฐœ์˜ reducer๊ฐ€ ์กด์žฌ ํ•  ๋•Œ ํ•˜๋‚˜๋กœ ํ•ฉ์ณ์„œ ํ• ๋‹น์‹œํ‚ค๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

๐Ÿ“ƒ PokemonReducer.ts

import {POKEMON_FAIL, POKEMON_SUCCESS,PokemonType,PokemonDispatchType} from '../actions/PokemonActionType'

interface InitialState {
    success:boolean
    pokemon?:PokemonType
}

const initialState : InitialState = {
    success:false
}

const PokemonReducer = (state=initialState, action:PokemonDispatchType) : InitialState=> {
    switch (action.type) {
        case POKEMON_FAIL :
            return{
                ...state,
                success:false
            }
        case POKEMON_SUCCESS :
          1๏ธโƒฃ const {abilities,sprites} = action.payload
            return {
                ...state,
                success:true,
                pokemon: {
                    abilities,
                    sprites
                }
            }
        default:
            return{
                ...state
            }
    }
}

export default PokemonReducer

๐Ÿ“‹ Memo(PokemonReducer.ts)

    1๏ธโƒฃ const {abilities,sprites} = action.payload
      1.abilities = action.payload.abiliteis
      2.sprites = action.payload.sprites

๐Ÿ“ actions

๐Ÿ“ƒ PokemonActionType.ts

1๏ธโƒฃ export const POKEMON_SUCCESS = 'POKEMON_SUCCESS'
   export const POKEMON_FAIL = 'POKEMON_FAIL'

2๏ธโƒฃ export type PokemonType = {
    abilities : PokemonAbility[]
    sprites : PokemonSprites
}

3๏ธโƒฃ export type PokemonAbility = {
    ability: {
        name:string,
        url:string,
    },
    is_hidden:boolean,
    slot:number
}
export type PokemonSprites = {
    front_default : string
}

4๏ธโƒฃ export interface pokemonFailDispatch {
    type : typeof POKEMON_FAIL
}
export interface pokemonSuccessDispatch {
    type : typeof POKEMON_SUCCESS
    payload: {
        abilities : PokemonAbility[],
        sprites : PokemonSprites
    }
}

5๏ธโƒฃ export type PokemonDispatchType = pokemonFailDispatch | pokemonSuccessDispatch

๐Ÿ“‹ Memo(PokemonActionType.ts)

    1๏ธโƒฃ export const POKEMON_SUCCESS = 'POKEMON_SUCCESS'...
      1. action์˜ type์„ ๋ณ€์ˆ˜๋กœ ์„ ์–ธํ•œ๋‹ค.
      
    2๏ธโƒฃ export type PokemonType
      1. fetchํ•œ data์—์„œ ์‚ฌ์šฉํ•  data์ธ abilities์™€ sprites์˜ type์„ ์„ ์–ธํ•œ๋‹ค.
    
    3๏ธโƒฃ export type PokemonAbility,export type PokemonSprites
      1. ability, is_hidden, slot, front_default์˜ type์„ ์„ ์–ธํ•œ๋‹ค.
    
    4๏ธโƒฃ export interface pokemonFailDispatch, export interface pokemonSuccessDispatch
      1. fetch๋ฅผ ์‹คํŒจ ํ–ˆ์„ ๋•Œ, ์„ฑ๊ณต ํ–ˆ์„ ๋•Œ์˜ action์˜ type์„ ์ •์˜ํ•œ๋‹ค.
      
    5๏ธโƒฃ export type PokemonDispatchType
      1. PokemonAction.ts ํŒŒ์ผ ์•ˆ์˜ dispatch ํ•จ์ˆ˜์˜ type์„ ์ •์˜ํ•œ๋‹ค.

๐Ÿ“ƒ PokemonAction.ts

import axios from 'axios'
import {Dispatch} from 'redux';
import {PokemonDispatchType, POKEMON_SUCCESS, POKEMON_FAIL} from './PokemonActionType';

export const fetchPokemonData = (pokemonName:string) => async (dispatch:Dispatch<PokemonDispatchType>) => {

    try {
        const res = await axios.get(`https://pokeapi.co/api/v2/pokemon/${pokemonName}`)
        const data = res.data

        1๏ธโƒฃ dispatch({
            type:POKEMON_SUCCESS,
            payload:data
        })

    } catch(err) {
        2๏ธโƒฃ dispatch({
            type:POKEMON_FAIL
        })
    }
}

๐Ÿ“‹ Memo(PokemonAction.ts)

    1๏ธโƒฃ dispatch => Success
      1. axios.get()์„ ์„ฑ๊ณตํ•˜์˜€์„ ๊ฒฝ์šฐ data์™€ ํ•จ๊ป˜ action ์ƒ์„ฑํ•จ์ˆ˜ ์—†์ด,
         type์ด POKEMON_SUCCESS์ธ action object๋ฅผ dispatchํ•œ๋‹ค.
    2๏ธโƒฃ dispatch => fail
      1. axios.get()์„ ์‹คํŒจํ•˜์˜€์„ ๊ฒฝ์šฐ type์ด POKEMON_FAIL์ธ action object๋ฅผ dispatchํ•œ๋‹ค.

๐Ÿ“ƒ App.tsx

import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {useState} from 'react';
import {RootReducerType} from './store'
import {fetchPokemonData} from './actions/PokemonAction'

function App() {

const [pokemonName, setPokemonName] = useState("");
1๏ธโƒฃ const pokemonReducer = useSelector((state : RootReducerType) => state.PokemonReducer)

2๏ธโƒฃ const dispatch = useDispatch();

const handlePokemonName = (event : React.ChangeEvent) => setPokemonName(event.target.value);

const searchButtonTapped = () => {
dispatch(fetchPokemonData(pokemonName))
}

return (

<div className="App">
  <input value = {pokemonName} onChange={handlePokemonName}/>
    <button onClick={searchButtonTapped}>ํฌ์ผ“๋ชฌ์ฐพ๊ธฐ</button>
      <div>
        {pokemonReducer.success && <div>
          <p>{pokemonName}</p>
          {pokemonReducer.pokemon?.abilities.map((ability, id) => {
            return <div key={id}><p>{ability.ability.name}</p>
            <p>{ability.slot}</p></div>
          })}
          <img src={pokemonReducer.pokemon?.sprites.front_default} />
          </div> }
      </div>
</div>

);
}

export default App;

#### ๐Ÿ“‹ Memo(App.tsx)
```typescript
    1๏ธโƒฃ const pokemonReducer = useSelector((state : RootReducerType) => state.PokemonReducer)
      1. PokemonReducer๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” state๋ฅผ useSelector hook์œผ๋กœ ์กฐํšŒํ•œ๋‹ค.
    2๏ธโƒฃ const dispatch = useDispatch();
      1. fetchPokemonData์˜ return ๊ฐ’์ธ action์„ dispatchํ•˜๊ธฐ ์œ„ํ•ด์„œ
         useDispatch hook์„ ์‚ฌ์šฉํ•œ๋‹ค.

์ข‹์€ ์›นํŽ˜์ด์ง€ ์ฆ๊ฒจ์ฐพ๊ธฐ