Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to prevent double rendering when importing my components?

I am creating a Todo List app saving data in localstorage but when i reaload the page, my components renders twice creating empty data and replacing by the previous tasks saved in localstorage.

This is my main file: main.jsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.jsx';

import './scss/styles.scss';
import * as bootstrap from 'bootstrap';

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
);

Also main.jsx’s child: App.jsx

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

import React from 'react';
import ToDo from './Components/ToDo';

const App = () => (
  <div className="container-fluid col-md-5 mx-auto" data-bs-theme="dark">
    <h1 className="text-center fw-bold">
      To do app
    </h1>
    <ToDo />
  </div>
);

export default App;

This is App.jsx’s child: ToDo.jsx

import React, { useState, useEffect } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faListCheck, faCirclePlus } from '@fortawesome/free-solid-svg-icons';
import SimpleToast from './Alerts/SimpleToast';
import Items from './Items';

const ToDo = () => {
  const [tasks, setTasks] = useState([]);
  const [newTask, setNewTask] = useState('');

  useEffect(() => {
    const storedTasks = localStorage.getItem('tasks');
    if (storedTasks) {
      setTasks(JSON.parse(storedTasks));
    }
  }, []);

  useEffect(() => {
    localStorage.setItem('tasks', JSON.stringify(tasks));
  }, [tasks]);

  const handleAddTask = (event) => {
    event.preventDefault();
    if (newTask.trim() === '') {
      SimpleToast.fire({
        icon: 'error',
        title: 'Enter a task first',
      });
      return;
    }
    setTasks([...tasks, { id: Date.now(), text: newTask, completed: false }]);
    setNewTask('');
  };
  const handleDeleteTask = (taskId) => {
    const updatedTasks = tasks.filter((task) => task.id !== taskId);
    setTasks(updatedTasks);
  };

  return (
    <>
      <form onSubmit={handleAddTask}>
        <div className="form-floating position-relative">
          <input
            id="new-task"
            type="text"
            className="form-control shadow-lg"
            placeholder="Enter a new task"
            value={newTask}
            onChange={(e) => setNewTask(e.target.value)}
          />
          <label htmlFor="new-task">
            Enter a new task
            {' '}
            <FontAwesomeIcon icon={faListCheck} />
          </label>
          <button
            type="submit"
            className="btn position-absolute top-50 end-0 translate-middle"
          >
            <FontAwesomeIcon
              type="submit"
              className="position-absolute translate-middle fs-3"
              icon={faCirclePlus}
            />
          </button>
        </div>
      </form>
      <Items tasks={tasks} handleDeleteTask={handleDeleteTask} />
    </>
  );
};

export default ToDo;

also the items components:

import React from 'react';
import Item from './Item';

const Items = ({ tasks, handleDeleteTask }) => (
  <ul className="list-group-flush bg-white rounded shadow-lg my-3 p-3">
    {tasks.length === 0 ? <li className="text-center list-group-item"> No tasks</li> : tasks.map((task) => (
      <Item key={task.id} task={task.text} handleDeleteTask={handleDeleteTask} idTask={task.id} />
    ))}
  </ul>
);

export default Items;

and the item component:

import React, { useState, useEffect } from 'react';
import { faPen, faTrash, faFloppyDisk } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

const Item = ({ task, handleDeleteTask, idTask }) => {
  const [text, setText] = useState('');
  const [isEdit, setIsEdit] = useState(false);
  const [isSelected, setIsSelected] = useState(false);

  useEffect(() => {
    setText(task);
  }, [task]);

  const handleEdit = () => {
    if (!isEdit) {
      setIsEdit(true);
      return;
    }
    setIsEdit(false);
  };

  const handleCheckTask = () => {
    if (!isSelected) {
      setIsSelected(true);
      return;
    }
    setIsSelected(false);
  };

  return (
    <li className="row border rounded shadow-md p-1 my-2">
      <div className="col-1 p-0 text-center align-middle">
        <input className="form-check-input align-middle" type="checkbox" onChange={() => handleCheckTask()} />
      </div>
      <div className="col-9 p-0">
        <input className={`form-control border-0 text-dark ${isSelected ? 'text-decoration-line-through' : 'text-decoration-none'}`} value={text} type="text" onChange={(e) => setText(e.target.value)} disabled={!isEdit} />
      </div>
      <div className="col-1 p-0">
        <button type="button" className="btn" onClick={() => handleEdit()}>
          <FontAwesomeIcon className="text-danger" icon={!isEdit ? faPen : faFloppyDisk} />
        </button>

      </div>
      <div className="col-1 p-0">
        <button type="button" className="btn">
          <FontAwesomeIcon className="text-danger" icon={faTrash} onClick={() => handleDeleteTask(idTask)} />
        </button>

      </div>
    </li>
  );
};

export default Item;

I have tried logging inside Todo.jsx first useEffect console.log(storedTasks) and when I reload i can see the previous tasks in console but after that a new empty array that i think is replacing the first array.

logging storedTasks

>Solution :

It looks like you’re overwriting tasks with the default value every time you load. A quick an easy solution is if you change your second useEffect to run only on a valid task like for instance if there is a value in tasks.

  useEffect(() => {
    if(tasks.length > 0){
      localStorage.setItem('tasks', JSON.stringify(tasks));
    }
  }, [tasks]);

update: i’m reading over this and i missed the case were you want to clear out all tasks. in that case you should do.

localStorage.setItem('tasks', "[]");

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading