ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 4. 기능 구현하기 - TodoList | 벨로퍼트
    Front-end/React.js 2020. 7. 23. 11:41
    반응형

     

    Context 를 만들어주었으니, 이제 Context 와 연동을 하여 기능을 구현해봅시다. Context 에 있는 state 를 받아와서 렌더링을 하고, 필요한 상황에는 특정 액션을 dispatch 하면 됩니다.

     

     

    TodoHead 완성하기

    TodoHead 에서는 done 값이 false 인 항목들의 개수를 화면에 보여줍니다.

    /src/components/TodoHead.js :

    import React from 'react';
    import styled from 'styled-components';
    import { useTodoState } from '../TodoContext';
    
    const TodoHeadBlock = styled.div`
      // ...
    
    function TodoHead() {
      const todos = useTodoState();
      const undoneTasks = todos.filter(todo => !todo.done);
    
      return (
        <TodoHeadBlock>
          <h1>2019년 7월 10일</h1>
          <div className="day">수요일</div>
          <div className="tasks-left">할 일 {undoneTasks.length}개 남음</div>
        </TodoHeadBlock>
      );
    }
    
    export default TodoHead;

    코드 저장 후, 브라우저에서 "할 일 2개 남음" 이 여전히 잘 보여지고 있는지 확인해보세요.

     

    그 다음에는, 날짜가 보여지는 부분을 작업해주겠습니다. 이 과정에서는 Date 의 toLocaleString 이라는 함수를 사용합니다.

    /src/components/TodoHead.js :

    import React from 'react';
    import styled from 'styled-components';
    import { useTodoState } from '../TodoContext';
    
    const TodoHeadBlock = styled.div`
      // ...
    `;
    
    function TodoHead() {
      //...
    
      const today = new Date();
      const dateString = today.toLocaleDateString('ko-KR', {
        year: 'numeric',
        month: 'long',
        day: 'numeric'
      });
      const dayName = today.toLocaleDateString('ko-KR', { weekday: 'long' });
    
      return (
        <TodoHeadBlock>
          <h1>{dateString}</h1>
          <div className="day">{dayName}</div>
          <div className="tasks-left">할 일 {undoneTasks.length}개 남음</div>
        </TodoHeadBlock>
      );
    }
    
    export default TodoHead;

    코드를 저장하고 오늘의 날짜가 잘 보여지는지 확인해보세요.

     

     

    TodoList 완성하기

    TodoList 에서는 state 를 조회하고 이를 렌더링해주어야 합니다. 그리고, onToggle, onRemove 와 같이 항목에 변화를 주는 작업은 이 컴포넌트에서 신경 쓸 필요 없습니다. 이 작업은 우리가 각 TodoItem 에서 해줄 것입니다.

    /src/components/TodoList.js :

    // ...
    import { useTodoState } from '../TodoContext';
    
    //... 
    
    function TodoList() {
      const todos = useTodoState();
    
      return (
        <TodoListBlock>
          {todos.map(todo => (
            <TodoItem
              key={todo.id}
              id={todo.id}
              text={todo.text}
              done={todo.done}
            />
          ))}
        </TodoListBlock>
      );
    }

    작업이 끝났습니다. 정말 간단하죠? 코드를 저장하고 항목들이 이전과 같이 오류없이 잘 나타나는지 확인하세요.

     

     

    TodoItem 완성하기

    이번에는 dispatch 를 사용해서 토글 기능과 삭제 기능을 구현해보겠습니다.

    /src/components/TodoItem.js :

    // ...
    import { useTodoDispatch } from '../TodoContext';
    
    // ...
    
    function TodoItem({ id, done, text }) {
      const dispatch = useTodoDispatch();
      const onToggle = () => dispatch({ type: 'TOGGLE', id });
      const onRemove = () => dispatch({ type: 'REMOVE', id });
      return (
        <TodoItemBlock>
          <CheckCircle done={done} onClick={onToggle}>
            {done && <MdDone />}
          </CheckCircle>
          <Text done={done}>{text}</Text>
          <Remove onClick={onRemove}>
            <MdDelete />
          </Remove>
        </TodoItemBlock>
      );
    }

    이제 기능이 잘 작동하는지 확인해보세요.

     

    잘 작동했다면, 맨 마지막줄 내보내는 부분에서 React.memo 를 사용해주세요.

    export default React.memo(TodoItem);

    이렇게 하면, 다른 항목이 업데이트 될 때, 불필요한 리렌더링을 방지하게 되어 성능을 최적화 할 수 있게 됩니다.

     

     

    TodoCreate 완성하기

    이번에는 TodoCreate 의 기능을 완성해줄 차례입니다. 이 컴포넌트에서는 자체적으로 관리해야 할 input 상태도 있습니다. 코드를 다음과 같이 작성해보세요.

    /src/components/TodoCreate.js :

    // ...
    import { useTodoDispatch, useTodoNextId } from '../TodoContext';
    
    // ...
    
    function TodoCreate() {
      const [open, setOpen] = useState(false);
      const [value, setValue] = useState('');
    
      const dispatch = useTodoDispatch();
      const nextId = useTodoNextId();
    
      const onToggle = () => setOpen(!open);
      const onChange = e => setValue(e.target.value);
      const onSubmit = e => {
        e.preventDefault(); // 새로고침 방지
        dispatch({
          type: 'CREATE',
          todo: {
            id: nextId.current,
            text: value,
            done: false
          }
        });
        setValue('');
        setOpen(false);
        nextId.current += 1;
      };
    
      return (
        <>
          {open && (
            <InsertFormPositioner>
              <InsertForm onSubmit={onSubmit}>
                <Input
                  autoFocus
                  placeholder="할 일을 입력 후, Enter 를 누르세요"
                  onChange={onChange}
                  value={value}
                />
              </InsertForm>
            </InsertFormPositioner>
          )}
          <CircleButton onClick={onToggle} open={open}>
            <MdAdd />
          </CircleButton>
        </>
      );
    }
    
    export default React.memo(TodoCreate);

    이 컴포넌트의 onSubmit 에서는 새로운 항목을 추가하는 액션을 dispatch 한 후, value 초기화 및 open 값을 false 로 전환해주었습니다.

     

    그리고 맨 마지막 줄에서는 React.memo 로 감싸주었는데요, 이렇게 함으로써 TodoContext 에서 관리하고 있는 state 가 바뀔 때 때 TodoCreate 의 불필요한 리렌더링을 방지 할 수 있습니다. 만약 우리가 Context 를 하나만 만들었다면 이런 최적화를 하지 못하게 됩니다.

     

    이제 이번 프로젝트의 모든 기능 구현이 끝났습니다!

    반응형

    댓글

Luster Sun