您好,如何在我使用 redux 的 todo 应用程序中正确实现 localStorage 的使用?

问题描述 投票:0回答:1

我遇到了以下问题:我使用 React 和 Redux 创建了一个待办事项应用程序,它运行良好,直到我决定实现 localStorage。当我启动我的应用程序时,我收到一个非常常见的错误:“未捕获错误:对象作为 React 子项无效(找到:带有键 {id、title、isCompleted、isEditing、number} 的对象)”。 如果您打算渲染子集合,请改用数组。但是,我不知道需要在哪里进行更改以防止出现此错误。我创建了一个减速器:

import {
  ADD_TASK,
  REMOVE_TASK,
  UPDATE_TASK,
} from "../actionsTypes";
import { v4 as uuidv4 } from "uuid";

export const initialState = {
  tasklist: [], //tasklist
};

export function taskReducer(state = initialState, action) {
  switch (action.type) {
    case ADD_TASK:
      return {
        ...state,
        tasklist: [
          ...state.tasklist,
          {
            id: uuidv4(),
            title: action.payload,
            isCompleted: false,
            isEditing: false,
            number: state.tasklist.length + 1,
          },
        ],
      };

    case REMOVE_TASK:
      return {
        ...state,
        tasklist: state.tasklist.filter((todo) => action.payload !== todo.id),
      };

    case UPDATE_TASK:
      return {
        ...state,
        tasklist: state.tasklist.map((prevTask) =>
          prevTask.id === action.payload.id
            ? { ...prevTask, title: action.payload.newTitle }
            : prevTask
        ),
      };

    default:
      return state;
  }
}

我还有我的 TaskList.jsx,我尝试在其中插入和从 LocalStorage 中提取数据:

import React, { useState, useEffect } from "react";
import InputTask from "../InputTask/InputTask";
import HeaderOfTaskList from "../HeaderOfTaskList/HeaderOfTaskList";
import Task from "../Task/Task";
import { useDispatch, useSelector } from "react-redux";
import { removeTask, addTask } from "../../store/actions";
import InputSearch from "../InputSearch/InputSearch";
import useInput from "../../hooks/useInput";
import "./TaskList.css";

export const TaskList = () => {
  const dispatch = useDispatch();
  const tasks = useSelector((state) => state.tasklist);
  const input = useInput();
  const [searchTerm, setSearchTerm] = useState("");

  useEffect(() => {
    const storedTasks = localStorage.getItem("tasks"); //JSON.stringify(localStorage.getItem("tasks"));
    if (storedTasks) {
      dispatch(addTask(JSON.parse(storedTasks)));
    }
  }, [dispatch, tasks]);

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

  const filteredTasks = tasks.filter((task) =>
    typeof task.title === "string" &&
    task.title.toLowerCase().includes(searchTerm.toLowerCase())
  );

  const isTaskListEmpty = filteredTasks.length === 0;

  const handleInputChange = (value) => {
    setSearchTerm(value);
  };

  const handleDelete = (id) => {
    dispatch(removeTask(id));
  };

  const handleAddTask = (tasklist) => {
    dispatch(addTask(tasklist));
  };

  return (
    <div>
      <InputTask addTask={handleAddTask} />
      <InputSearch onInputChange={handleInputChange} />
      <HeaderOfTaskList />
      {!isTaskListEmpty ? (
        <ul>
          {filteredTasks
            .filter((task) =>
              task.title.toLowerCase().includes(input.value.toLowerCase())
            )
            .map((task) => (
              <Task
                task={task}
                key={task.id}
                onDelete={() => handleDelete(task.id)}
              />
            ))}
        </ul>
      ) : (
        <ul>
          {tasks.map((task) => (
            <Task
              task={task}
              key={task.id}
              onDelete={() => handleDelete(task.id)}
            />
          ))}
        </ul>
      )}
    </div>
  );
};

我的任务组件负责渲染任务列表:

import React, { useState } from "react";
import { useDispatch } from "react-redux";
import Button from "@mui/material/Button";
import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit";
import { Checkbox } from "@mui/material";
import { updateTask } from "../../store/actions";
import "./Task.css";

const Task = ({ task, onDelete }) => {
  const [isEditing, setIsEditing] = useState(false);
  const [inputValue, setInputValue] = useState(task.title);
  const [theme, setTheme] = useState("dark");
  const dispatch = useDispatch();

  const onEdit = () => {
    setIsEditing(!isEditing);
  };

  const onCheckBoxClicked = () => {
    if (!task.isCompleted) {
      setTheme(theme === "light" ? "dark" : "light");
    }
  };

  const onSaveClicked = () => {
    dispatch(updateTask(task.id, inputValue));
    setIsEditing(!isEditing);
  };

  return (
    <li className={theme}>
      {isEditing ? (
        <input
          id="edittask"
          type="text"
          value={inputValue}
          onChange={(e) => {
            setInputValue(e.target.value);
          }}
          onKeyDown={(e) => {
            if (e.key === "Enter" && isEditing) {
              onSaveClicked();
            }
          }}
        />
      ) : (
        <>
          {task.number}
          <p>{task.title}</p>
          <Checkbox onClick={onCheckBoxClicked} />
        </>
      )}
      {!isEditing ? (
        <Button onClick={onEdit} variant="outlined" endIcon={<EditIcon />}>
          Edit
        </Button>
      ) : (
        <Button onClick={onSaveClicked} variant="outlined">
          Save
        </Button>
      )}
      <Button onClick={onDelete} variant="outlined" endIcon={<DeleteIcon />}>
        Remove
      </Button>
    </li>
  );
};

export default Task;
import React, { useState } from "react";
import "./InputTask.css";
import Button from "@mui/material/Button";

const InputTask = ({ addTask }) => {
  const [value, setValue] = useState("");

  const handleChange = () => {
    if (value) {
      addTask(value);
    }
    setValue("");
  };

  const isEnterButtonClicked = (e) => {
    if (e.key === "Enter") {
      addTask(value);
      setValue("");
    }
  };

  return (
    <div className="sectioninput">
      <input
        value={value}
        type="text"
        id="typetask"
        placeholder="What are you gonna do?"
        onChange={(e) => setValue(e.target.value)}
        onKeyDown={isEnterButtonClicked}
      />
      <Button variant="outlined" onClick={handleChange}>
        Add Task
      </Button>
    </div>
  );
};

export default InputTask;

导入{ 添加任务, 删除_任务, 更新任务, 完成_任务, } 来自“./actionsTypes”;

导出 const addTask = (标题) => ({ 类型:ADD_TASK, 有效负载:标题, });

导出常量removeTask =(id)=>({ 类型:REMOVE_TASK, 有效负载:ID, });

导出 const updateTask = (id, 标题) => ({ 类型:更新任务, 有效负载:{id,newTitle:标题}, });

导出 const CompleteTask = (id) => ({ 类型:COMPLETE_TASK, 有效负载:ID, });

enter code here
javascript reactjs redux local-storage
1个回答
0
投票

创建 localstorage 的自定义钩子,或者我个人更喜欢 localforage,然后是 localstorage。 LocalForage 使用异步存储,例如 IndexedDB 或 WebSQL,具有与 localStorage 类似的简单 API。

© www.soinside.com 2019 - 2024. All rights reserved.