JS Library/React

Context API 활용 상태 관리

원2 2022. 8. 3. 14:53
728x90
반응형

앞의 게시글과 이어지는 context API reducer 를 만들겠음ㅇ

 

TodoContext.js

import React, {useReducer} from "react";

const initialTodos = [
    {
        id: 1,
        text: '프로젝트 생성',
        done: true
    },
    {
        id: 2,
        text: '컴포넌트 스타일링하기',
        done: true
    },
    {
        id: 3,
        text: 'Context 만들기',
        done: false
    },
    {
        id: 4,
        text: '기능 구현하기',
        done: false
    },
]

function todoReducer(state, action) {
    switch (action.type) {
        case 'CREATE':
            return state.concat(action.todo);
        case 'TOGGLE':
            return state.map(todo =>
                todo.id === action.id ? {...todo, done: !todo.done} : todo
            );
        case 'REMOVE':
            return state.filter(todo => todo.id !== action.id);
        default:
            throw new Error(`Unhandled action type: ${action.type}`);
    }
}

export function TodoProvider({children}) {
    const [state, dispatch] = useReducer(todoReducer, initialTodos);
    return children;
}

 

Context 생성

state, dispatch 를 Context 를 통해 다른 컴포넌트에서 바로 사용 할 수 있게 만들것임

두개의 Context 를 생성하여 불필요한 렌더링을 방지

import React, {createContext, useReducer} from "react";

const initialTodos = [
    {
        id: 1,
        text: '프로젝트 생성',
        done: true
    },
    {
        id: 2,
        text: '컴포넌트 스타일링하기',
        done: true
    },
    {
        id: 3,
        text: 'Context 만들기',
        done: false
    },
    {
        id: 4,
        text: '기능 구현하기',
        done: false
    },
]

function todoReducer(state, action) {
    switch (action.type) {
        case 'CREATE':
            return state.concat(action.todo);
        case 'TOGGLE':
            return state.map(todo =>
                todo.id === action.id ? {...todo, done: !todo.done} : todo
            );
        case 'REMOVE':
            return state.filter(todo => todo.id !== action.id);
        default:
            throw new Error(`Unhandled action type: ${action.type}`);
    }
}

const TodoStateContext = createContext();
const TodoDispatchContext = createContext();

export function TodoProvider({children}) {
    const [state, dispatch] = useReducer(todoReducer, initialTodos);
    return (
        <TodoStateContext.Provider value={state}>
            <TodoDispatchContext.Provider value={dispatch}>
                {children}
            </TodoDispatchContext.Provider>
        </TodoStateContext.Provider>

    );
}

Context 에서 사용 할 값을 지정 할 때에는 Provider 컴포넌트를 렌더링하고 value를 설정

> 그 내부에 children 렌더링 > 다른 컴포넌트 사용

// 이런식으로 다른 컴포넌트에서 사용가능
import React, { useContext } from 'react';
import { TodoStateContext, TodoDispatchContext } from '../TodoContext';

function Sample() {
  const state = useContext(TodoStateContext);
  const dispatch = useContext(TodoDispatchContext);
  return <div>Sample</div>;
}

 

 

커스텀 Hook 만들기

컴포넌트에서 useContext 를 직접 사용하지 않고, useContext를 사용하는 커스텀 Hook 을 생성

import React, {createContext, useContext, useReducer} from "react";

const initialTodos = [
    {
        id: 1,
        text: '프로젝트 생성',
        done: true
    },
    {
        id: 2,
        text: '컴포넌트 스타일링하기',
        done: true
    },
    {
        id: 3,
        text: 'Context 만들기',
        done: false
    },
    {
        id: 4,
        text: '기능 구현하기',
        done: false
    },
]

function todoReducer(state, action) {
    switch (action.type) {
        case 'CREATE':
            return state.concat(action.todo);
        case 'TOGGLE':
            return state.map(todo =>
                todo.id === action.id ? {...todo, done: !todo.done} : todo
            );
        case 'REMOVE':
            return state.filter(todo => todo.id !== action.id);
        default:
            throw new Error(`Unhandled action type: ${action.type}`);
    }
}

const TodoStateContext = createContext();
const TodoDispatchContext = createContext();

export function TodoProvider({children}) {
    const [state, dispatch] = useReducer(todoReducer, initialTodos);
    return (
        <TodoStateContext.Provider value={state}>
            <TodoDispatchContext.Provider value={dispatch}>
                {children}
            </TodoDispatchContext.Provider>
        </TodoStateContext.Provider>

    );
}

// 커스텀 Hook
export function useTodoState() {
    return useContext(TodoStateContext);
}

export function useTodoDispatch() {
    return useContext(TodoDispatchContext);
}

ㄱ 그럼 나중에 이렇게 쓸 수 있다

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

function Sample() {
  const state = useTodoState();
  const dispatch = useTodoDispatch();
  return <div>Sample</div>;
}

위의 코드에 비해서 간결해졌음, 근데 이건 취향이라서 편한대로 사용하면 될듯

 

nextId 값 관리

나중에 create를 할때 필요한 Id 값인 nextId 를 만들어주자 고유 ID 가 필요하기 때문ㅇㅇ

import React, {createContext, useContext, useReducer, useRef} from "react";

const initialTodos = [
    {
        id: 1,
        text: '프로젝트 생성',
        done: true
    },
    {
        id: 2,
        text: '컴포넌트 스타일링하기',
        done: true
    },
    {
        id: 3,
        text: 'Context 만들기',
        done: false
    },
    {
        id: 4,
        text: '기능 구현하기',
        done: false
    },
]

function todoReducer(state, action) {
    switch (action.type) {
        case 'CREATE':
            return state.concat(action.todo);
        case 'TOGGLE':
            return state.map(todo =>
                todo.id === action.id ? {...todo, done: !todo.done} : todo
            );
        case 'REMOVE':
            return state.filter(todo => todo.id !== action.id);
        default:
            throw new Error(`Unhandled action type: ${action.type}`);
    }
}

const TodoStateContext = createContext();
const TodoDispatchContext = createContext();
const TodoNextIdContext = createContext();

export function TodoProvider({children}) {
    const [state, dispatch] = useReducer(todoReducer, initialTodos);
    const nextId = useRef(5)
    return (
        <TodoStateContext.Provider value={state}>
            <TodoDispatchContext.Provider value={dispatch}>
                <TodoNextIdContext.Provider value={nextId}>
                    {children}
                </TodoNextIdContext.Provider>
            </TodoDispatchContext.Provider>
        </TodoStateContext.Provider>

    );
}

// 커스텀 Hook
export function useTodoState() {
    return useContext(TodoStateContext);
}

export function useTodoDispatch() {
    return useContext(TodoDispatchContext);
}

export function useTodoNextId() {
    return useContext(TodoNextIdContext);
}

 

커스텀 Hook 에서 에러 처리 (선택)

import React, {createContext, useContext, useReducer, useRef} from "react";

const initialTodos = [
    {
        id: 1,
        text: '프로젝트 생성',
        done: true
    },
    {
        id: 2,
        text: '컴포넌트 스타일링하기',
        done: true
    },
    {
        id: 3,
        text: 'Context 만들기',
        done: false
    },
    {
        id: 4,
        text: '기능 구현하기',
        done: false
    },
]

function todoReducer(state, action) {
    switch (action.type) {
        case 'CREATE':
            return state.concat(action.todo);
        case 'TOGGLE':
            return state.map(todo =>
                todo.id === action.id ? {...todo, done: !todo.done} : todo
            );
        case 'REMOVE':
            return state.filter(todo => todo.id !== action.id);
        default:
            throw new Error(`Unhandled action type: ${action.type}`);
    }
}

const TodoStateContext = createContext();
const TodoDispatchContext = createContext();
const TodoNextIdContext = createContext();

export function TodoProvider({children}) {
    const [state, dispatch] = useReducer(todoReducer, initialTodos);
    const nextId = useRef(5)
    return (
        <TodoStateContext.Provider value={state}>
            <TodoDispatchContext.Provider value={dispatch}>
                <TodoNextIdContext.Provider value={nextId}>
                    {children}
                </TodoNextIdContext.Provider>
            </TodoDispatchContext.Provider>
        </TodoStateContext.Provider>

    );
}

// 커스텀 Hook
export function useTodoState() {
    const context = useContext(TodoStateContext);
    if (!context) {
        throw new Error(`Cannot find TodoProvider`);
    }
    return context;
}

export function useTodoDispatch() {
    const context = useContext(TodoDispatchContext);
    if (!context) {
        throw new Error(`Cannot find TodoProvider`);
    }
    return context;
}

export function useTodoNextId() {
    const context = useContext(TodoNextIdContext);
    if (!context) {
        throw new Error(`Cannot find TodoProvider`);
    }
    return context;
}

  꼭 이렇게 할 필욘없지만 나중에 오류 찾기 쉬움 

근데 난 안할거임ㅇㅇ 귀찮음ㅋ

 

컴포넌트 TodoProvider 감싸기

Todo 관련 Context 를 사용 할 수 있게 App 에서 TodoProvider 로 감싸기

import './App.css';
import {createGlobalStyle} from "styled-components";
import TodoTemplate from "./components/TodoTemplate";
import TodoHead from "./components/TodoHead";
import TodoList from "./components/TodoList";
import TodoCreate from "./components/TodoCreate";
import {TodoProvider} from "./components/TodoContext";

const GlobalStyle = createGlobalStyle`
  body {
    background: #e9ecef;
  }
`;

function App() {
    return (
        <TodoProvider>
            <GlobalStyle/>
            <TodoTemplate>
                <TodoHead/>
                <TodoList />
                <TodoCreate />
            </TodoTemplate>
        </TodoProvider>
    );
}

export default App;

TodoHead 에서 사용해보기

import React from "react";
import styled from "styled-components";
import {useTodoState} from "./TodoContext";

const TodoHeadBlock = styled.div`
    padding-top: 48px;
    padding-left: 32px;
    padding-right: 24px;
    padding-left: 32px;
    border-bottom: 1px solid #e9ecef;
    h1 {
        margin: 0;
        font-size: 36px;
        color: #343a40;
    } 
    .day {
        margin-top: 4px;
        color: #868e96;
        font-size: 21px;
    }
    .tasks-left {
        color: #20c997;
        font-size: 18px;
        margin-top: 40px;
        font-weight: bold;
    }
`;

function TodoHead() {
    const todos = useTodoState();
    console.log(todos)
    return (
        <TodoHeadBlock>
            <h1>2022년 08월 03일</h1>
            <div className="day">수요일</div>
            <div className="tasks-left">할일이 3개 남았소</div>
        </TodoHeadBlock>
    );
}

export default TodoHead;

좋았죠?

 

728x90
반응형

'JS Library > React' 카테고리의 다른 글

React Router  (0) 2022.08.04
TodoList 기능 구현  (0) 2022.08.03
todolist 만들기  (0) 2022.08.03
styled-components  (0) 2022.08.02
react-icons link  (0) 2022.08.01
CSS Module  (0) 2022.08.01