@pandycrowl

Оцените пожалуйста мой первый проект «Crud To-do list» на React.js + Node.js + Postgresql?

Я создал свой первый проект на Реакте с запросами на сервер. Оцените пожалуйста, может есть какие-то недочеты, чего-то не хватает или что-то лишнее, или вообще есть более легкий способ реализации. Буду рад пообщаться.

App.js

import { useState } from 'react'
import Todoform from './Components/TodoForm'

function App() {
  const [todo, setTodo] = useState([])

  const todoLength = (todos) => {
    if (todos) {
      setTodo(todos)
    }
  }
  return (
    <div className="App">

      <header>
        <h1>Список задач: {todo.length} </h1>
      </header>
      <Todoform todoLength={todoLength} />
    </div>
  );
}

export default App;


Компонент TodoForm.js
import { useState, useEffect } from 'react'
import ModalInput from './ModalInput';

function Todoform({ todoLength }) {
    const [modalActive, setModalActive] = useState(false)
    const [error, setError] = useState(null)
    const [isLoaded, setIsLoaded] = useState(false)
    const [todos, setTodos] = useState([])
    const [userInput, setUserInput] = useState('')
    const [userInputUpdate, setUserInputUpdate] = useState('')
    const [todo, setTodo] = useState([])
    const [isChecked, setIsChecked] = useState(false);

    const handleOnChange = () => {
        setIsChecked(!isChecked);
    };

    useEffect(() => {
        getTodos()
    }, [])

    const getTodos = () => {
        fetch('http://localhost:3001/api/todo', {
            method: 'GET',
        })
            .then(res => res.json())
            .then(
                (result) => {
                    setIsLoaded(true);
                    setTodos(result);
                    todoLength(result)
                },
                (error) => {
                    setIsLoaded(true);
                    setError(error);
                }
            )
    }
    const createTodo = () => {
        fetch('http://localhost:3001/api/todo', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                todo_text: userInput,
                complete: false
            }),
        })
            .then((res) => res.json())
            .then(result => {
                setTodos([...todos, result])
                getTodos()
            },
                (error) => {
                    setIsLoaded(true)
                    setError(error)
                }
            )
    }
    const updateTodo = (id, todo_text, complete) => {
        fetch(`http://localhost:3001/api/todo`, {
            method: 'PUT',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                todo_text: todo_text,
                complete: complete,
                id: id
            }),
        })
            .then((res) => res.json())
            .then(result => {
                setTodos([...todos])
                getTodos()
            },
                (error) => {
                    setIsLoaded(true)
                    setError(error)
                }
            )
    }
    const removeTodo = (id) => {
        fetch(`http://localhost:3001/api/todo/${id}`, {
            method: 'DELETE',
        })
            .then(data => {
                // alert(data)
                getTodos()
            })
    }
    const handleChange = (e) => {
        setUserInput(e.currentTarget.value)
    }
    const handleSubmit = (e) => {
        e.preventDefault()
        if (userInput) {
            setUserInput('')
            createTodo()
        }
    }
    const handleKeyPress = (e) => {
        if (e.key === 'Enter') {
            handleSubmit(e)
        }
    }
    const handleModalChange = (e) => {
        setUserInputUpdate(e.currentTarget.value)
    }
    const handleModalSubmit = (event) => {
        event.preventDefault()
        if (userInputUpdate) {
            setUserInputUpdate('')
            updateTodo(todo[0], userInputUpdate, isChecked)
            setModalActive(false)
        }

    }
    const handleKeyPressmodal = (e) => {
        if (e.key === 'Enter') {
            handleModalSubmit(e)
        }
    }
    if (error) {
        return <div>Ошибка: {error.message}</div>;
    } else if (!isLoaded) {
        return <div>Загрузка...</div>;
    } else if (todos) {
        return (
            <div>
                <form onSubmit={handleSubmit}>
                    <input
                        className='input'
                        value={userInput}
                        type="text"
                        onChange={handleChange}
                        onKeyDown={handleKeyPress}
                        placeholder="Введите текст"
                    />
                    <button className="item-save">Сохранить</button>
                </form>
                {todos.map((todo) => {
                    return (<div className="todos" >
                        <div key={todo.id} className="item-todo" >
                            <div className={todo.complete ? "item-text strike" : "item-text"} >
                                {todo.todo_text}  {todo.complete}
                            </div>
                            <div className='todo-checkbox'>
                                <input className='checkbox' type="checkbox" checked={todo.complete ? true : false} onChange={() => { updateTodo(todo.id, todo.todo_text, !todo.complete); setIsChecked(!todo.complete) }} />
                            </div>
                        </div>

                        <button className="item-update" onClick={() => { setModalActive(true); setTodo([todo.id, todo.todo_text, todo.complete]); setUserInputUpdate(todo.todo_text); setIsChecked(todo.complete) }}>
                            Изменить
                        </button>
                        <button className="item-delete" onClick={() => removeTodo(todo.id)}>
                            X
                        </button>
                    </div>)
                })}
                <ModalInput active={modalActive} setActive={setModalActive}>
                    <form onSubmit={handleModalSubmit}>
                        <input
                            className='input'
                            value={userInputUpdate}
                            type="text"
                            onChange={handleModalChange}
                            onKeyDown={handleKeyPressmodal}
                            placeholder="Введите текст"
                        />
                        <input className='checkbox' type="checkbox" checked={isChecked ? true : false} onChange={handleOnChange} />
                        <button className="item-save">Сохранить</button>
                    </form>
                </ModalInput>
            </div>
        )
    }
}

export default Todoform


ModalInput.js
const ModalInput = ({ active, setActive, children }) => {
    return (
        <div className={active ? "modal active" : "modal"} onClick={() => setActive(false)}>
            <div className={active ? "modal__content active" : "modal__content"} onClick={e => e.stopPropagation()}>
                {children}
            </div>
        </div>
    )
}

export default ModalInput


Ссылка на полный проект с серверной частью: https://github.com/pandycrowl/Crud-ToDo-List.git

И у меня есть пару вопросов:

1. Я видел, что для файлов компонентов используют расширение .jsx, но jsx работает и с раcширением .js. Я что-то не понял или без разницы какое расширение?
2. Я не знаю как избавиться от двух предупреждений: 1)"Warning: Each child in a list should have a unique "key" prop. Check the render method of Todoform." 2) "React Hook useEffect has a missing dependency: 'getTodos'. Either include it or remove the dependency array react-hooks/exhaustive-deps"
  • Вопрос задан
  • 498 просмотров
Решения вопроса 1
black1277
@black1277
Вольный стрелок
По вопросам:
1 Особой разницы нет, только для вашей IDE - она правильнее подсказки будет делать в файле jsx
2 Первое предупреждение о том, что используете key={todo.id} немного не там - нужно в родительский div это переместить
<div className="todos" вот сюда>
       <div key={todo.id} className="item-todo" >

второе предупреждение связано с тем, что хук в своем массиве зависимостей не видит вызываемую в нем функцию
useEffect(() => {
        getTodos()
    }, [вот тут])

пока можете поставить такую строку, чтобы не видеть ошибку
useEffect(() => {
        getTodos()
// eslint-disable-next-line
    }, [])

Если хотите разобраться с правильным использованием useEffect
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@budda674
1) асинхронные запросы нужно оборачивать в useMemo или useCallback
2)checked={isChecked ? true : false} сокращай до checked={isChecked}
3) у списка ключ не в том месте, <div className="todos" > - сюда ключ
4) для запросов в базу лучше использовать sequelize
5) всю инфу из db.js нужно хранить в .env
6) проще юзать пакет cors
7) к проекту нужно описывать реадми с инфой как его запускать
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы