React 3.

AWESOMee·2022년 7월 1일
0

React

목록 보기
3/6
post-thumbnail

Todo list


terminal

npm create react-app todo-app
npm install sass
npm install classnames
npm install react-icons


directory


TodoTemplate.js

TodoTemplate.js

import React from 'react';
import '../styles/TodoTemplate.scss'

const TodoTemplate = ({children}) => {
    return (
        <div className='TodoTemplate'>
            <div className='app-title'>🤍Schedule Management🤍</div>
            <div className='content'>{children}</div>
        </div>
    );
};

export default TodoTemplate;

TodoTemplate.scss

.TodoTemplate {
    width: 512px;
    margin-left: auto;
    margin-right: auto;
    margin-top: 6rem;
    border-radius: 4px;
    overflow: hidden;

    .app-title {
        background-color: #b778ee;
        color: white;
        height: 4rem;
        font-size: 1.5rem;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .content {
        background-color: white;

    }
}

TodoInsert.js

TodoInsert.js

import React, { useCallback, useState } from 'react';
import {MdAdd} from 'react-icons/md';
import '../styles/TodoInsert.scss';

const TodoInsert = ({onInsert}) => {
    const [value, setValue] = useState('');

    const changeValue = useCallback(e => {
        setValue(e.target.value);
    }, []); // callback when the first rendering

    const handleSubmit = useCallback(e => {
        onInsert(value);
        setValue('');

        // defending re-load
        e.preventDefault();
    }, [onInsert, value]);

    return (
        <form className='TodoInsert' onSubmit={handleSubmit}>
            <input 
                type="text" 
                placeholder='Enter the Task' 
                value={value} 
                onChange={changeValue}/> 
            <button type='submit'>
                <MdAdd/>
            </button>
        </form>
    );
};

export default TodoInsert;

TodoInsert.scss

.TodoInsert {
    display: flex;
    background-color: #514957;

    input {
        background: none;
        outline: none;
        border: none;
        padding: 0.5rem;
        font-size: 1.125rem;
        line-height: 1.5;
        color: white;

        &::placeholder {
            color: #e3dee6;
        }

        // every area except for button
        flex: 1;
    }

    button {
        background: #968296;
        outline: none;
        border: none;
        color: white;
        padding-left: 1rem;
        padding-right: 1rem;
        font-size: 1.5rem;
        display: flex;
        align-items: center;
        cursor: pointer;
        transition: 0.1s background ease-in;

        &:hover {
            background: #b7adbd;
        }
    }
}

TodoListItem.js

TodoListItem.js

import React from 'react';
import {
    MdCheckBoxOutlineBlank,
    MdCheckBox,
    MdRemoveCircleOutline
} from 'react-icons/md';
import '../styles/TodoListItem.scss';
import cn from 'classnames';

const TodoListItem = ({todo, onRemove, onChecked}) => {
    const {id, text, checked} = todo;

    return (
        <div className='TodoListItem'>
            <div className={cn('checkbox', {checked})} onClick={() => onChecked(id)}>
                {checked ? <MdCheckBox/> : <MdCheckBoxOutlineBlank/>}
                <div className='text'>{text}</div>
            </div>
            <div className='remove' onClick={() => onRemove(id)}>
                <MdRemoveCircleOutline/>
            </div>
        </div>
    );
};

export default TodoListItem;

TodoListItem.scss

.TodoListItem {
    padding: 1rem;
    display: flex;
    align-items: center;

    &:nth-child(even) {
        background: #f9f8fa;
    }

    .checkbox {
        cursor: pointer;
        flex: 1;
        display: flex;
        align-items: center;

        svg {
            font-size: 1.5rem;
        }

        .text {
            margin-left: 0.5rem;
            flex: 1;
        }

        &.checked {
            svg {
                color: #ac22cf;
            }
            
            .text {
                color: #b7adbd;
                text-decoration: line-through;
            }
        }
    }

    .remove {
        display: flex;
        align-items: center;
        font-size: 1.5rem;
        color: #ff6b6b;
        cursor: pointer;

        &:hover {
            color: #ff9b87;
        }
    }

    // components in the same level
    & + & {
        border-top: 1px solid #e2dee6;
    }
}

TodoList.js

TodoList.js

import React from 'react';
import TodoListItem from './TodoListItem';
import '../styles/TodoList.scss'

const TodoList = ({todos, onRemove, onChecked}) => {
    return (
        <div className='TodoList'>
            {todos.map(todo => (
                <TodoListItem 
                    todo={todo} 
                    key={todo.id} 
                    onRemove={onRemove} 
                    onChecked={onChecked}
                />
            ))}
        </div>
    );
};

export default TodoList;

TodoList.scss

.TodoList {
    min-height: 320px;
    max-height: 513px;
    overflow-y: auto;
}

App.js

// import logo from './logo.svg';
import { useCallback, useRef, useState } from 'react';
import './App.css';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
import TodoTemplate from './components/TodoTemplate';

function App() {
   const [todos, setTodos] = useState([
    {
        id: 1,
        text: 'react basic',
        checked: true,
    },
    {
        id: 2,
        text: 'styling the components',
        checked: true,
    },
    {
        id: 3,
        text: 'making a todo app',
        checked: false,
    },
   ])

   const nextId = useRef(4);

   const onInsert = useCallback(text => {
    const todo = {
        id: nextId.current,
        text: text,
        checked: false,
    };
    setTodos(todos.concat(todo));
    nextId.current += 1;
   }, [todos]);

   const onRemove = useCallback(
    id => {
        setTodos(todos.filter(todo => todo.id !== id));
    },
    [todos]
   );

   const onChecked = useCallback(
    id => {
        setTodos(todos.map(todo => todo.id === id ? {...todo, checked: !todo.checked} : todo))
    },
    [todos]
   );

  return (
    <TodoTemplate>
      <TodoInsert onInsert={onInsert}/>
      <TodoList todos={todos} onRemove={onRemove} onChecked={onChecked}/>
    </TodoTemplate>
  );
}

export default App;

.....

컴포넌트 최적화 한거 분명 작성했는데 왜 날라갔지.....

component optimize

UseState

TodoListItem.js

export default React.memo(TodoListItem);

App.js

// import logo from './logo.svg';
import { useCallback, useRef, useState } from 'react';
import './App.css';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
import TodoTemplate from './components/TodoTemplate';

function createBultTodos() {
  const array = [];
  for(let i = 1; i <= 2500; i++) {
    array.push({
      id: i,
      text: `task ${i}`,
      checked: false,
    });
  }
  return array;
}

function App() {
  const [todos, setTodos] = useState(createBultTodos);

  const nextId = useRef(2501);

  const onInsert = useCallback(
    (text) => {
      const todo = {
        id: nextId.current,
        text: text,
        checked: false,
      };
      setTodos((todos) => todos.concat(todo));
      nextId.current += 1;
    },
    [],
  );

  const onRemove = useCallback(
    (id) => {
      setTodos((todos) => todos.filter((todo) => todo.id !== id));
    },
    [],
  );

  const onChecked = useCallback(
    (id) => {
      setTodos(
        (todos) =>
        todos.map((todo) =>
          todo.id === id ? { ...todo, checked: !todo.checked } : todo,
        ),
      );
    },
    [],
  );

  return (
    <TodoTemplate>
      <TodoInsert onInsert={onInsert} />
      <TodoList todos={todos} onRemove={onRemove} onChecked={onChecked} />
    </TodoTemplate>
  );
}

export default App;

ver2 (not using useState)

TodoList.js

export default React.memo(TodoList);

App.js

// import logo from './logo.svg';
import { useCallback, useReducer, useRef, useState } from 'react';
import './App.css';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
import TodoTemplate from './components/TodoTemplate';

function createBultTodos() {
  const array = [];
  for (let i = 1; i <= 2500; i++) {
    array.push({
      id: i,
      text: `task ${i}`,
      checked: false,
    });
  }
  return array;
}

function todoReducer(todos, action) {
  switch (action.type) {
    case 'INSERT':
      return todos.concat(action.todo);
    case 'REMOVE':
      return todos.filter((todo) => todo.id !== action.id);
    case 'CHECKED':
      return todos.map((todo) =>
        todo.id === action.id ? { ...todo, checked: !todo.checked } : todo,
      );
    default:
      return todos;
  }
}

function App() {
  // 초기상태를 undefined로 설정
  // 세번째 파라미터에 초기상태 만드는 함수를 넣으면, 첫 렌더링에만 해당 함수 호출
  const [todos, dispatch] = useReducer(todoReducer, undefined, createBultTodos);

  const nextId = useRef(2501);

  const onInsert = useCallback((text) => {
    const todo = {
      id: nextId.current,
      text: text,
      checked: false,
    };
    dispatch({ type: 'INSERT', todo });
    nextId.current += 1;
  }, []);

  const onRemove = useCallback((id) => {
    dispatch({ type: 'REMOVE', id });
  }, []);

  const onChecked = useCallback((id) => {
    dispatch({ type: 'CHEKCED', id });
  }, []);

  return (
    <TodoTemplate>
      <TodoInsert onInsert={onInsert} />
      <TodoList todos={todos} onRemove={onRemove} onChecked={onChecked} />
    </TodoTemplate>
  );
}

export default App;
profile
개발을 배우는 듯 하면서도

0개의 댓글