0

Goal: I am trying to persist data using local storage with React.

Approach: Here's my approach to using local storage to store data. First, I have the first useEffect that triggers a localStorage to store an array of items, which are checkboxes. This happens every time a user adds a new toDo checkbox. Secondly, I have another useEffect that listens to the current location of the page. Whenever a user navigates to another page and comes back to the current page, useEffect will trigger the localStorage.get('tasks') and store the objects in the array to the hook called setToDo. This will render items that were stored previously with local storage.

Problem: The problem I keep getting is Objects are not valid as React child even though I tried appending all the objects in the array from localStorage.get('tasks') to the "toDo" hook by using array destructing. Ex. setToDo(prevItems=> [...prevItems, ...value] within the second useEffect. This should be able to render the list of objects in the array using the map function as displayed in the code. Also, items in localStorage are being stored because I checked it with Chrome. Please help me because I've been struggling a lot and have tried every solution(s) possible, yet no results.

Image of error message https://i.sstatic.net/cMuPi.jpg Quick Note: The item_value shown in the image holds a component called PriorityLists, which renders a checkbox with textarea as shown here-> https://i.sstatic.net/p057C.jpg

Code This code should suffice to solve the problem.

import React, { useState, useEffect } from 'react';
import { Delete, Refresh, Add } from '../components/Actions';
import { Header } from '../components/Header';
import { PriorityLists } from '../components/PriorityLists';
import { v4 as uuidv4 } from 'uuid';

function Task() {
  const [toDo, setToDo] = useState([]);
  const [idsToRefresh, setIdsToRefresh] = useState([]);
  const [filter_items, setFilterItems] = useState(false);
  const [ids, setIds] = useState([]);

  const [currentPath, setCurrentPath] = useState(window.location.pathname);
  useEffect(() => {
    if (toDo[0] !== undefined) {
      localStorage.setItem('tasks', JSON.stringify(toDo));
    }
  }, [toDo]);

  useEffect(() => {
    const { pathname } = window.location;
    console.log('path');
    setCurrentPath(pathname);
    if (JSON.parse(localStorage.getItem('tasks'))) {
      const value = JSON.parse(localStorage.getItem('tasks'));
      setToDo((prevItems) => [...prevItems, ...value]);
      console.log(value);
    }
  }, [window.location.pathname]);

  useEffect(() => {
    if (toDo[0] !== undefined) {
      setToDo(
        toDo.filter((item) => {
          return !ids.includes(item._Id);
        })
      );
    }
  }, [filter_items]);

  function addIds(checked, id_to_be_deleted) {
    if (!checked) {
      setIds((item) => [...item, id_to_be_deleted]);
    } else {
      setIds(
        ids.filter((item) => {
          return item !== id_to_be_deleted;
        })
      );
    }
  }

  function addToDos() {
    const id = uuidv4();

    setToDo(
      toDo.concat({
        _Id: id,
        item_value: <PriorityLists id={id} checked={false} addIds={addIds} />,
      })
    );
    setIdsToRefresh(idsToRefresh.concat(id));
  }

  function refresh() {
    setToDo(
      toDo.filter((item) => {
        return !idsToRefresh.includes(item._Id);
      })
    );
  }

  return (
    <div className="main-content">
      <div className="container-fluid">
        <div className="row underline">
          <div className="col">
            <div className="row">
              <div className="col-3 pt-2">
                <Refresh _refresh={refresh} />
              </div>

              <div className="col-6 text-center">
                <Header header={'Tasks'} />
              </div>

              <div className="col-3 pt-2">
                <button className="float-right">
                  <Delete
                    setFilterItems={setFilterItems}
                    filter={filter_items}
                  />
                </button>
              </div>
            </div>
          </div>
        </div>

        <div className="row">
          <div className="col">
            {toDo.map((item) => {
              return (
                <div key={item._Id}>
                  <ul>
                    <li>{item.item_value}</li>
                  </ul>
                </div>
              );
            })}
          </div>
        </div>

        <div className="row">
          <div className="col pr-4">
            <Add addToDos={addToDos} />
          </div>
        </div>
      </div>
    </div>
  );
}

export default Task;

1 Answer 1

0

Check the error message again and the offending line:

line: <li>{item.item_value}</li> error: Objects are not valid as React child.

Looking at your screenshot item_value is a deconstructed JSX:

<PriorityLists id = {id} checked = {false} addIds = {addIds} />

You need the list of tasks to hold simple objects so you can then ask React to render them for example:

<li><PriorityLists id={item.id} checked={item.checked} addIds={this.addIds} />

React will say that any time you do something as simple as <>{object}</> if your object is structured as expected then you need to extract the attributes that you want, e.g. <>{object.title}</>.

I hope that guides you in the correct solution.

Sign up to request clarification or add additional context in comments.

6 Comments

I don't quite understand your example. I don't get the object error before using localStorage. It was still the same methodology with respect to getting the component using item.item_value.
What he tried to say, basically, is that you can't store JSX elements in the localStorage and then render them when you fetch them. What you can do is store an object with the data you need to render and then loop with that
@luissmg That was pretty much what I wanted to do- to store an array of objects that has a property called item_value that stores a JSX element. Then, I can render them using the map function. But the problem is I keep getting React can't render objects when in fact it is an array of objects that hold a JSX element inside a property.
@Ron you won't be able to store JSX elements in localStorage, you might be able to do with some hacks but the preferable and easy way would be to store the serializable props that the React component needs to render.
@DaniloCabello Ok. I've tried storing the local storage with keys of the component and rendering the components from toDos that matches the keys in local storage. The result I get is empty. I need a way to store and render the component later on. How do I do this?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.