typescript contextAPI로 todolist 만들기

nevermind·2022년 11월 11일
0

typescript

목록 보기
6/12
post-custom-banner

2개의 context

상태변경을 위해서는 2개의 context를 먼저 만들어야 한다.
하나는 상태 전용 context, 다른 하나는 디스패치 전용 context이다

2개를 만드는 이유는 디스패치 함수를 사용했을 시에만 필요한 컴포넌트 상태도 업데이트될때 리랜더링되기 때문이다.

//ContextTodo.tsx
import React,{createContext, useContext, useReducer,Dispatch, ReactNode} from 'react'

type State = {
  id: number;
  content: string;
  complete: boolean
}

type ArrayState = State[]

type Action = 
  | { type: 'Add_Todo'; content: string }
  | { type: 'Remove_Todo'; id: number }
  | { type: 'Toggle_complete'; id: number }

type todoDispatch = Dispatch<Action>

const stateContext = createContext<ArrayState | null>(null)
const dispatchContext = createContext<todoDispatch | null>(null)

createContext함수의 Generics를 사용하여 Context에서 관리할 값 상태를 설정해준다.
우리가 추후 Provider를 사용하지 않았을 때에는 Context 값이 null이 되어야하기에 <ArrayState | null>로 선언

액션타입을 선언해준다(추가, 삭제, 완료상태변경)

type todoDispatch = Dispatch<Action>을 불러와 Generic으로 액션들의 타입을 넣어주면 컴포넌트에서 액션을 디스패치할 때 액션들에 대한 타입을 검사할 수 있다.(contentid값이 빠지면 오류발생)

리듀서 작성

//ContextTodo.tsx
import React,{createContext, useContext, useReducer,Dispatch, ReactNode} from 'react'

type State = {
  id: number;
  content: string;
  complete: boolean
}

type ArrayState = State[]

type Action = 
  | { type: 'Add_Todo'; content: string }
  | { type: 'Remove_Todo'; id: number }
  | { type: 'Toggle_complete'; id: number }

type todoDispatch = Dispatch<Action>

const stateContext = createContext<ArrayState | null>(null)
const dispatchContext = createContext<todoDispatch | null>(null)

export function ContextTodoProvider ({children} : {children : ReactNode})  {
  const [state, dispatch] = useReducer(reducer, [{
    id: 0,
    content: '블라블라',
    complete: false
  }])

  return (
    <stateContext.Provider value={state}>
      <dispatchContext.Provider value={dispatch}>
        {children}
      </dispatchContext.Provider>
   </stateContext.Provider>
  )
}

ContextTodoProvider 만들기

export function ContextTodoProvider ({children} : {children : ReactNode})  {
  const [state, dispatch] = useReducer(reducer, [{
    id: 0,
    content: '블라블라',
    complete: false
  }])

  return (
    <stateContext.Provider value={state}>
      <dispatchContext.Provider value={dispatch}>
        {children}
      </dispatchContext.Provider>
   </stateContext.Provider>
  )
}

App.tsx에 불러와서 기존 내용을 감싸주기 위해 export를 시켜준다

커스텀 hooks 두개 작성하기

stateContext,dispatchContext를 사용하기 위해 useContext를 사용해준다.

export function useTodo() {
  const state = useContext(stateContext)
  if(!state) throw new Error('Cannot find sampleProvider')
  return state
}

export function useTodoDispatch() {
  const dispatch = useContext(dispatchContext)
  if(!dispatch) throw new Error('Cannot find sampleProvider')
  return dispatch
}

사용하기

//ContextInput.tsx
import React, { useState } from 'react'
import {useTodo, useTodoDispatch} from './ContextTodo'
import '../App.css'
import ContextList from './ContextList'

const ContextInput = () => {
  //useContext불러오기
  const todo = useTodo();
  const todoDispatch = useTodoDispatch();

  const [inputs, setInputs] = useState<string>('')

  const inputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setInputs(e.target.value)
  } 

  const submit = (e:any) => {
    e.preventDefault();
    if (!inputs) return 
    
    //상태 변경
    todoDispatch({type:'Add_Todo', content: inputs })

    setInputs('')
    
  }
  return (
    <div>
    <form onSubmit={submit}>
        <input type="text" className='inputStyle' onChange={inputChange} value={inputs} />
        
      <button className='btnStyle' type='submit'>+</button>
    </form>
    {todo?.map((x, i) => <ContextList data={x} key={i} todo={todo} />)}
    
  </div>  )
}

export default ContextInput
//ContextList.tsx
import React from 'react'
import { useTodoDispatch} from './ContextTodo'

interface  Content  {
  id: number,
  content: string,
  complete: boolean
};


const ContextList = ({ data }: { data: Content }) => {
  const todoDispatch = useTodoDispatch();

  const isDone = () => {
    todoDispatch({type: 'Toggle_complete', id: data.id})
  }

  const remove = () => {
    console.log(data.id)
    todoDispatch({ type: 'Remove_Todo', id: data.id })
  } 
  return (
    <div className='Container'>
    {data.complete ? <div className='toggleLine'>{data.content}</div> : <div>{data.content}</div>}
    <div>
      <button className='noStyleBtn' onClick={isDone}></button>
      <button className='noStyleBtn' onClick={remove}></button>
    </div>
  </div>
  )
}

export default ContextList

참조 : https://velog.io/@velopert/typescript-context-api

profile
winwin
post-custom-banner

0개의 댓글