처음부터 시작하는 Java
  • Context API 활용 상태 관리
    2022년 08월 03일 14시 53분 23초에 업로드 된 글입니다.
    작성자: 원2
    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
    댓글