[TS / React] Context API

2022. 7. 28. 09:40·Stack/TypeScript

1. 컨텍스트 만들기

const stateContext = createContext(null)

 

<stateContext.Provider value = {state}>

                <App/>

</stateContext.Provider>

 

2. 컨텍스트 사용하기

const state = useContext(stateContext)

 

TS-REACT-TUTORIAL

SampleContext.tsx

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

type Color = 'red' | 'orange' | 'green'
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: 'TOGGLE_GOOD'}

// 디스패치를 위한 타입 (Dispatch)
type SampleDispatch = Dispatch<Action>

// Context 만들기
const SampleStateContext = createContext<State | null>(null)
const SampleDispatchContext = createContext<SampleDispatch | null>(null)

function reducer(state: State, action: Action) : State {
    switch(action.type) {
        case 'SET_COUNT':
            return {
                ...state,
                count: action.count
            }
        case 'SET_TEXT':
            return {
                ...state,
                text: action.text
            }
        case 'SET_COLOR':
            return {
                ...state,
                color: action.color
            }
        case 'TOGGLE_GOOD':
            return {
                ...state,
                isGood: !state.isGood
            }
        default: 
            throw new Error('액션이 없어요')
    }
}

export function SampleProvider({children}:{children:React.ReactNode}) {
    const [state, dispatch] = useReducer(reducer, {
        count: 0,
        text: 'Hello',
        color: 'red',
        isGood: true
    });
    return (
        <SampleStateContext.Provider value = {state}>
            <SampleDispatchContext.Provider value={dispatch}>
                {children}
            </SampleDispatchContext.Provider>
        </SampleStateContext.Provider>
    )
}

// state와 dispatch를 쉽게 사용하기위한 커스텀 hook
export function useSampleState() {
    const state = useContext(SampleStateContext);
    if(!state) throw new Error('찾을 수 없어') // 유효하지 않을 땐 에러를 발생
    return state
}
export function useSampleDispatch() {
    const dispatch = useContext(SampleDispatchContext);
    if(!dispatch) throw new Error('찾을 수 없어') // 유효하지 않을 땐 에러를 발생
    return dispatch
}

 

ReducerSample.tsx

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

// 기존 useReducer
// 이제 SampleContext로부터 받아온 것을 사용해서 다 주석처리함
// type Color = 'red' | 'orange' | 'green'
// 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: 'TOGGLE_GOOD'}

// function reducer(state: State, action: Action) : State {
//     switch(action.type) {
//         case 'SET_COUNT':
//             return {
//                 ...state,
//                 count: action.count
//             }
//         case 'SET_TEXT':
//             return {
//                 ...state,
//                 text: action.text
//             }
//         case 'SET_COLOR':
//             return {
//                 ...state,
//                 color: action.color
//             }
//         case 'TOGGLE_GOOD':
//             return {
//                 ...state,
//                 isGood: !state.isGood
//             }
//         default: 
//             throw new Error('액션이 없어요')
//     }
// }
const ReducerSample = () => {
    // const [state, dispatch] = useReducer(reducer, {
    //     count: 0,
    //     text: 'Hello',
    //     color: 'red',
    //     isGood: true
    // });

    const state = useSampleState();
    const dispatch = useSampleDispatch();

    const setCount = () => dispatch({ type: 'SET_COUNT', count:5 })
    const setText = () => dispatch({ type:'SET_TEXT', text: 'bye' })
    const setColor = () => dispatch({ type: 'SET_COLOR', color: 'orange' })
    const toggleGood = () => dispatch({ type:'TOGGLE_GOOD' })
    return (
        <div>
            <p>
                <code>count : </code> {state.count}
            </p>
            <p>
                <code>text : </code> {state.text}
            </p>
            <p>
                <code>color : </code> {state.color}
            </p>
            <p>
                <code>isGood : </code> {state.isGood? 'true' : 'false'}
            </p>
            <div>
                <button onClick={setCount}>setcount</button>
                <button onClick={setText}>setText</button>
                <button onClick={setColor}>setcolor</button>
                <button onClick={toggleGood}>toggleGood</button>
            </div>
        </div>
    );
};

export default ReducerSample;

 

App.tsx

import React, { useReducer } from 'react';
import './App.css';
// import CreateTodo from './CreateTodo';
import Counter from './Counter';
import Counter_Reduce from './Counter_Reduce';
import ReducerSample from './ReducerSample';
import MyForm from './MyForm';
import { SampleProvider } from './SampleContext';

function App() {
  return (
    <div className="App">
      {/* <Counter /> */}
      {/* <MyForm /> */}
      {/* <Counter_Reduce /> */}
      <SampleProvider>
        <ReducerSample/>
      </SampleProvider>
    </div>
  );
}

export default App;

 


 

todosContext.tsx

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

// 상태 관리할 데이터
// 1. input의 값
// 2. 할 일 목록

export type Todo = {
    id: number;
    text: string;
    isDone: boolean;
}

type State = {
    inputText: string;
    todos: Todo[]
}

// 액션
// input 값이 변경될 때 inputText 변경- INPUT_CHANGE
// 등록버튼 누르면 할 일 추가 - CREATE_TODO
// 삭제버튼 누르면 할 일 삭제 - DELETE_TODO
// 할 일 항목 클릭시 isDone 값을 반전 - DONE_TODO
type Action = { type: 'INPUT_CHANGE'; inputText: string }
| { type: 'CREATE_TODO'; todo: Todo }
| { type: 'DELETE_TODO'; id: number }
| { type: 'DONE_TODO'; id: number }

type TypeDispatch = Dispatch<Action>

// Context 만들기
const todoStateContext = createContext<State|null>(null);
const todoDispatchContext = createContext<TypeDispatch|null>(null);

function reducer(state:State, action: Action) :State {
    switch(action.type) {
        case 'INPUT_CHANGE':
            console.log(state.inputText)
            return {
                ...state,
                inputText: action.inputText
            }
        case 'CREATE_TODO':
            return {
                ...state,
                todos: [
                    ...state.todos,
                    action.todo
                ]
            }
        case 'DELETE_TODO':
            return {
                ...state,
                todos: state.todos.filter(todo=> todo.id !== action.id)
            }
        case 'DONE_TODO':
            return {
                ...state,
                todos: state.todos.map(todo=> todo.id === action.id
                    ? {...todo, isDone: !todo.isDone } : todo)
            }
        default:
            throw new Error("액션이 없어요")
    }
}

// todoStateContext state를 지정
// todoDispatchContext dispatch를 지정
export function TodoContext({children}: {children: React.ReactNode}) {
    const [state, dispatch]=useReducer(reducer, {
        inputText: "",
        todos: [{
            id: 1,
            text: "타입스크립트 공부",
            isDone: false
        },{
            id: 2,
            text: "리덕스 공부",
            isDone: false
        }]
    })
    return (
        <todoStateContext.Provider value={state}>
            <todoDispatchContext.Provider value={dispatch}>
                {children}
            </todoDispatchContext.Provider>
        </todoStateContext.Provider>
    )
}

// state와 dispatch를 쉽게 사용하기 위한 커스텀hooks
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/TodoList.tsx

import React from 'react';
import { Todo } from '../todosContext'

type TodoProps = {
    todos: Todo [];
    onDelete(id:number):void;
    onToggle(id:number):void;
}
const TodoList = ( {todos, onDelete, onToggle} : TodoProps ) => {
    return (
        <div>
            <ul>
                {todos.map(todo=> <li key={todo.id} className={todo.isDone? "active": ""}><span onClick={()=>onToggle(todo.id)}>{todo.text}</span><button onClick={()=>onDelete(todo.id)}>삭제</button></li>)}
                {/* key 안줘서 나는 오류 key={todo.id} 추가하기 */}
            </ul>
        </div>
    );
};

export default TodoList;

 

components/InsertTodo.tsx

import React from 'react';

type InsertProps = {
    inputText: string;
    onChange(text:string): void;
    onCreate(): void
}
const InsertTodo = ({ inputText, onChange, onCreate } : InsertProps) => {
    return (
        <div>
            <input value={inputText} onChange={(e)=>onChange(e.target.value)}/>
            <button onClick={onCreate}>등록</button>
        </div>
    );
};

export default InsertTodo;

 

App3.tsx

import React, { useRef } from 'react';
import { useTodoDispatch, useTodoState } from './todosContext';
import InsertTodo from './components/InsertTodo';
import TodoList from './components/TodoList';
import './App.css';

// 상태 관리할 데이터
// 1. input의 값
// 2. 할 일 목록

// export type Todo = {
//     id: number;
//     text: string;
//     isDone: boolean;
// }

// type State = {
//     inputText: string;
//     todos: Todo[]
// }

// // 액션
// // input 값이 변경될 때 inputText 변경- INPUT_CHANGE
// // 등록버튼 누르면 할 일 추가 - CREATE_TODO
// // 삭제버튼 누르면 할 일 삭제 - DELETE_TODO
// // 할 일 항목 클릭시 isDone 값을 반전 - DONE_TODO

// type Action = { type: 'INPUT_CHANGE'; inputText: string }
// | { type: 'CREATE_TODO'; todo: Todo }
// | { type: 'DELETE_TODO'; id: number }
// | { type: 'DONE_TODO'; id: number }

// function reducer(state:State, action: Action) :State {
//     switch(action.type) {
//         case 'INPUT_CHANGE':
//             console.log(state.inputText)
//             return {
//                 ...state,
//                 inputText: action.inputText
//             }
//         case 'CREATE_TODO':
//             return {
//                 ...state,
//                 todos: [
//                     ...state.todos,
//                     action.todo
//                 ]
//             }
//         case 'DELETE_TODO':
//             return {
//                 ...state,
//                 todos: state.todos.filter(todo=> todo.id !== action.id)
//             }
//         case 'DONE_TODO':
//             return {
//                 ...state,
//                 todos: state.todos.map(todo=> todo.id === action.id
//                     ? {...todo, isDone: !todo.isDone } : todo)
//             }
//         default:
//             throw new Error("액션이 없어요")
//     }
// }

const App3 = () => {
    // const [state, dispatch] = useReducer(reducer, {
    //     inputText: "",
    //     todos: [{
    //         id: 1,
    //         text: "타입스크립트 공부",
    //         isDone: false
    //     },{
    //         id: 2,
    //         text: "리덕스 공부",
    //         isDone: false
    //     }]
    // })
    const idNum = useRef(2);
    const state = useTodoState();
    const dispatch = useTodoDispatch();
    const { inputText, todos } = state;
    const onChange = (text: string) => dispatch({ type: 'INPUT_CHANGE', inputText: text })
    const onCreate = () => {
        idNum.current++;
        console.log(idNum.current)
        dispatch({type:'CREATE_TODO', todo: {
        id: idNum.current,
        text: state.inputText,
        isDone: false
    }})
    }
    const onDelete = (id: number) => dispatch({ type: 'DELETE_TODO', id: id})
    const onToggle = (id: number) => dispatch({ type: 'DONE_TODO', id: id})
    return (
            <div>
                <InsertTodo inputText={inputText} onChange={onChange} onCreate={onCreate}/>
                <TodoList todos={todos} onDelete={onDelete} onToggle={onToggle}/>
            </div>
    );
};

export default App3;

 

App.css

.active {
  background-color: antiquewhite;
}

 

index.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import reportWebVitals from './reportWebVitals';
// import App from './App';
// import App2 from './App2';
import App3 from './App3';
import { TodoContext } from './todosContext';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    {/* <App /> */}
    {/* <App2 /> */}
    <TodoContext>
      <App3 />
    </TodoContext>
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

'Stack > TypeScript' 카테고리의 다른 글

[TS / React] Redux로 To-do List 구현  (0) 2022.07.29
[TS / React] Redux로 Counter 구현  (0) 2022.07.28
[TS / React] To-Do List 구현  (0) 2022.07.27
[TS / React] props 전달  (0) 2022.07.27
[TS / React] React에서 TS 사용해보기 2 (useReducer)  (0) 2022.07.27
'Stack/TypeScript' 카테고리의 다른 글
  • [TS / React] Redux로 To-do List 구현
  • [TS / React] Redux로 Counter 구현
  • [TS / React] To-Do List 구현
  • [TS / React] props 전달
7ingout
7ingout
  • 7ingout
    Hello, 7ingout world!
    7ingout
  • 전체
    오늘
    어제
    • 분류 전체보기 (205)
      • Project (5)
      • Stack (173)
        • React (40)
        • JavaScript (50)
        • TypeScript (14)
        • HTML (11)
        • CSS (31)
        • Spring (9)
        • PHP (15)
        • SQL (3)
        • Python (0)
      • ETC (9)
      • Design (13)
        • Illustrator (6)
        • Photoshop (7)
      • Articloid (4)
        • 7ingout (4)
  • 공지사항

    • ☻
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
7ingout
[TS / React] Context API
상단으로

티스토리툴바