본문 바로가기
typescript

[typescript] react-typscript(useReducer + contextAPI)

by 남민섭 2023. 2. 24.
728x90
반응형

useReducer 알아보기

2023.01.14 - [react] - [React] Hook 함수정리3(useReducer)

 

[React] Hook 함수정리3(useReducer)

useReducer를 알기전에 상태를 업데이트 할때는 useState를 사용해서 새로운 상태를 설정해주었는데 상태를 관리하게 될 때 useState를 사용하는거 말고도 또 다른 방법 useReducer!!!!! 새로운 상태관리~~ u

uou413.tistory.com

contextAPI 알아보기

2023.01.10 - [react] - [React]Hooks함수 정리1(useState, useRef, useEffect, useContext)

 

[React]Hooks함수 정리1(useState, useRef, useEffect, useContext)

용어 정리 ● 렌더(render) : 화면에 그리다(생성) ● 리렌더링/업데이트: 재정의/변경 (변수가 바뀔때는 리렌더링 안함!!!) 변경 하려면!!!!!! 1) 부모요소가 주는 props가 변경 되었을 때!!! 2) 상태가

uou413.tistory.com

 

실습연습

 

 

 

 

context 폴더 안에

SampleContext.tsx

import React, { Children, Dispatch, useContext, useReducer } from 'react';
import { createContext } from 'react';

//필요한 타입 선언
type Color = "red" | "pink" | "yellow";
//상태를 위한 타입
type State = {
    count: number;
    text: string;
    color: Color;
    isGood: boolean;
}
//액션을 위한 타입
type Action = {type: "SET_COUNT"; count: number}
| {type: "SET_TEXT"; text: string}
| {type: "SET_COLOR"; color: Color}
| {type: "SET_GOOD"}

//디스패치를 위한 타입(Dispatch를 리액트에서 불러올수 있음)
// 액션들의 타입을 Dispatch 제네릭으로 지정
type SampleDispatch = Dispatch<Action>;

//context 생성하기
const SampleStateContext = createContext<State | null>(null);
const SampleDispatchContext = createContext<SampleDispatch | null>(null);

//리듀서  ----  매개변수 state와 action 타입 지정, 리턴하는 타입을 지정
function reducer(state: State, action:Action): State {
    switch(action.type) {
        case "SET_COUNT":
            return {
                ...state,
                count: action.count
            };
        case "SET_COLOR":
        return {
            ...state,
            color: action.color
        };
        case "SET_TEXT":
        return {
            ...state,
            text: action.text
        };
        case "SET_GOOD":
        return {
            ...state,
            isGood: !state.isGood
        };
        default:
            return state    
    }
}
/* interface SampleContextProps {
    children: React.ReactNode
} */ //인터페이스 안만들고 객체로 바로 넣음(인터페이스로 만들어도됨)

const SampleContext = ({children}:{children: React.ReactNode}) => {  //children : 타입지정
    const [state, dispatch] = useReducer(reducer, {
        count: 0,
        color: "pink",
        text: "typescript",
        isGood: true
    })
    return (
        <SampleStateContext.Provider value={state}>
            <SampleDispatchContext.Provider value={dispatch}>
                       {children}         
            </SampleDispatchContext.Provider>
        </SampleStateContext.Provider>
    );
};

export default SampleContext;

//state 와 dispatch 를 사용하기 위한 커스텀 hooks
export function useSampleState(){
    const state =useContext(SampleStateContext)
    return state;
}
export function useSampleDispatch(){
    const dispatch = useContext(SampleDispatchContext)
    if(!dispatch) throw new Error("유효하지 않습니다") 
    // 유효하지 않을때 에러 발생
    return dispatch
}

 

components폴더 안에

ReducerSampleEdit.tsx

import React from 'react';
import { useSampleDispatch, useSampleState } from '../context/SampleContext';

const ReducerSampleEdit = () => {
    const state = useSampleState()
    const dispatch = useSampleDispatch()
    const setCount = () => {
        dispatch({type: "SET_COUNT", count: 5})
    }
    const setText = () => {
        dispatch({type: "SET_TEXT", text: "blue"})
    }
    const setColor = () => {
        dispatch({type: "SET_COLOR", color: "yellow"})
    }
    const setToggle = () => {
        dispatch({type: "SET_GOOD"})
    }
    return (
        <div>
            <p>count: {state!.count}</p> 
            //뒤에!는 null과 undefiend가 오지 않는다는 의미. 값이 무조건 온다
            <p>text: {state!.text}</p>
            <p>color: {state!.color}</p>
            <p>isGood: {state!.isGood ? "true": "false"}</p>
            <div>
                <button onClick={setCount}>SET_COUNT</button>
                <button onClick={setText}>SET_TEXT</button>
                <button onClick={setColor}>SET_COLOR</button>
                <button onClick={setToggle}>SET_ISGOOD</button>
            </div>
        </div>
    );
};

export default ReducerSampleEdit;

 

App.tsx

import React from "react";

import ReducerSampleEdit from "./components/ReducerSampleEdit";

import SampleContext from "./context/SampleContext";

function App() {
  return (
    <div className="App">
      <SampleContext>
        <ReducerSampleEdit />
      </SampleContext>
    </div>
  );
}

export default App;

 


 

todolist 실습하기

 

 

 

context폴더 안에

TodoContext2.tsx

import React, { Dispatch, useContext, useReducer } from 'react';
import { createContext } from 'react';

const initialState = [
    {
      id: 1,
      text: "이력서, 자소서 쓰기",
      isDone: false
    },
    {
      id: 2,
      text: "이력서, 자소서 쓰기",
      isDone: false
    },
    {
      id: 3,
      text: "이력서, 자소서 쓰기",
      isDone: false
    }
  ]

//상태를 위한 타입
type State = {
    id: number,
    text: string,
    isDone: boolean
}
//액션을 위한 타입
type Action = {type: "ADDTODO"; todo: State}
| {type: "TOGGLETODO"; id: number}
| {type: "DELTODO"; id: number}

//디스패스 위한 타입
type TodoDispatch = Dispatch<Action>

//Context 생성
const TodoStateContext = createContext<State[] | null>(null); //괄호안 null은 초기값
const TodoDispatchContext = createContext<TodoDispatch | null>(null);

//reducer함수
function reducer(state : State[], action: Action): State[]{
    switch(action.type){
        case "ADDTODO":
          return [
            ...state,
            action.todo  
          ];
        case "DELTODO":
          return state.filter(li=>li.id !== action.id);
        case "TOGGLETODO":
          return state.map(li=> li.id === action.id ? { 
            ...li, isDone: !li.isDone}: li)
        default:
          return state;      
      }
}
/* interface TodoContextProps {
    children: React.ReactNode
} */ //인터페이스 안만들고 객체로 바로 넣음(인터페이스로 만들어도됨)

const TodoContext2 = ({children}:{children:React.ReactNode}) => {  //children : 타입지정
    const [state, dispatch] = useReducer(reducer, initialState)
    return (
        <TodoStateContext.Provider value={state}>  
            <TodoDispatchContext.Provider value={dispatch}>
                {children}
            </TodoDispatchContext.Provider>
        </TodoStateContext.Provider>
    );
};

export default TodoContext2;


//state와 dispatch를 리턴해주는 훅
export function useTodoState(){
    const state = useContext(TodoStateContext)
    if(!state) throw new Error("유효하지 않습니다")
    return state;
}

export function useTodoDispatch() {
    const dispatch = useContext(TodoDispatchContext)
    if(!dispatch) throw new Error("유효하지 않습니다")
    return dispatch
}

 

components폴더 안에

TodoInput.tsx

import React, { useRef, useState } from 'react';
import { useTodoDispatch } from '../context/TodoContext2';

const TodoInput = () => {
    //인풋의 값을 관리할 상태생성
    const [text, setText] = useState("")
    const dispatch = useTodoDispatch();
    //인풋의 값이 변경될때 호출되는 함수
    //text값을 인풋의 값으로 업데이트
    const onChange = (e:React.ChangeEvent<HTMLInputElement>) => {
        const t = e.target.value
        setText(t)
    }
    const ref = useRef<number>(4)
    const onAddTodo = () => {
        dispatch({type:"ADDTODO", todo:{
            id: ref.current,
            text: text,
            isDone: false
        }})
        //인풋 초기화
        setText("")
        //ref.current +1
        ref.current++
    }
    return (
        <div>
            <input name="text" value={text} onChange={onChange}/>
            <button onClick={onAddTodo}>등록</button>
        </div>
    );
};

export default TodoInput;

 

components폴더에서

TodoLists2.tsx

import React from 'react';
import { useTodoDispatch, useTodoState } from '../context/TodoContext2';

const TodoLists2 = () => {
    const state = useTodoState()//useContext()
    const dispatch = useTodoDispatch()
    const onDelTodo = (id:number) => {
        dispatch({type: "DELTODO", id:id})
    }
    const onToggleTodo = (id: number) => {
        dispatch({type: "TOGGLETODO", id:id})
    }
    return (
        <div>
            <ul>
                {state.map(todo=><li key={todo.id} 
                style={{background: todo.isDone ? "yellow" : undefined}}>
                    <span onClick={()=>{onToggleTodo(todo.id)}}>{todo.text}</span>
                    <button onClick={()=>{onDelTodo(todo.id)}}>삭제</button></li>)}
            </ul>
        </div>
    );
};

export default TodoLists2;

 

App.tsx

import React from 'react';
import TodoInput from './components/TodoInput';
import TodoLists2 from './components/TodoLists2';
import TodoContext2 from './context/TodoContext2';

const App = () => {
    return (
        <div>
            <TodoContext2>
                <TodoInput/>
                <TodoLists2/>
            </TodoContext2>
        </div>
    );
};

export default App;

 

728x90
반응형

댓글