useEffect очищает localstorage

Суть: когда я создаю новый todo он записывается в localstorage и после перезагрузки страницы хочу чтобы сохранённые todo отображались но localstorage очищается после перезагрузки страницы.

MainPage.tsx

'use client'
import React, { FormEvent, forwardRef, useEffect, useRef, useState } from 'react'
import { Box, Button, Stack, TextField, Typography } from '@mui/material'
import styles from './mainPage.module.scss'
import { useTodo } from '../../hooks/useTodo'
import { describe } from 'node:test'

const MainPage = () => {

    useEffect(() => {
        set(JSON.parse(localStorage.getItem('todoArray') || '[]'))
    }, [])

    const { functions, get, set } = useTodo()
    const titleRef = useRef<HTMLInputElement>()
    const descRef = useRef<HTMLInputElement>()


   

    const sendTodo = (e: FormEvent) => {
        e.preventDefault()
        if (!titleRef.current?.value || !descRef.current?.value) return
        functions.create(titleRef.current.value, descRef.current.value)
        titleRef.current.value = ''
        descRef.current.value = ''
    }

    return (
        <div className={styles.root}>
            <Stack className={styles.form} spacing={1} component='form' onSubmit={(e) => sendTodo(e)}>
                <TextField inputRef={titleRef} sx={{ width: '30%' }} id="outlined-basic" label="Outlined" variant="outlined" />
                <TextField inputRef={descRef} sx={{ width: '30%' }} id="outlined-basic" label="Outlined" variant="outlined" />
                <Button sx={{ width: '30%' }} type='submit' variant='outlined'>Create</Button>
            </Stack>
            <div className={styles.todoView}>
                {get.map((todo, i) =>
                    <Box key={i} className={styles.todoBody}>
                            <Stack gap={1} flexDirection='row' alignItems='center'>
                                <Typography variant='h6'>{i + 1}.</Typography>
                                <Typography variant='h6'>{todo.title}</Typography>
                            </Stack>
                            <Typography sx={{ wordWrap: 'break-word' }}>{todo.description}</Typography>
                            <Button sx={{ mt: 1 }} variant='contained' onClick={() => functions.delete(todo.id)} color='error'>Delete</Button>
                    </Box>
                )}
            </div>
        </div>
    )
}

export default MainPage

useView.ts

import React, { FormEvent, useEffect } from "react"


export interface ITodo {
    id: number
    title: string
    description: string
}

export interface IUseTodoReturn {
    functions: {
        create: (title: string, description: string) => void
        delete: (id: number) => void
    }
    get: ITodo[]
    set: (array: ITodo[]) => void
}


export const useTodo = (): IUseTodoReturn => {

    const [todo, setTodo] = React.useState<ITodo[]>([])

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

    const set = (array: ITodo[]) => {
        setTodo(array)
    }

    const createTodo = (title: string, description: string): void => {
        setTodo([...todo, { id: Date.now(), title, description }])
    }


    const deleteTodo = (id: number) => {
        const filteredArray = todo.filter(todo => todo.id !== id)
        setTodo(filteredArray)
    }


    return {
        functions: { create: createTodo, delete: deleteTodo },
        get: todo,
        set
    }
}

Ответы (2 шт):

Автор решения: Максим
const [todo, setTodo] = React.useState<ITodo[]>([])

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

как я понимаю ты просто перезаписываешь стор при обновлении страницы todo у тебя пустой соответственно у тебя todoArray: [] добавь проверку по типу

if (localStorage.getItem("todoArray") === null ) {
   localStorage.setItem('todoArray', JSON.stringify(todo))
}
→ Ссылка
Автор решения: Armen

Если ты используеш localStorage, то локально в коде хранить 'tasks' не совсем правильно, как один единственный источник правды должен выступать localStorage, канечно могут быть случии когда useEffect и все такое будет уместным но не в данном случее

import { useState } from "react";

export const App = () => {
  const { addTasks, tasks } = useTasks()
  return <div></div>;
};

const useTasks = () => {
  const { forceUpdate  } = useForceUpdate()  
  const tasks = localStorage.getItem("tasks") ?? [];

  const addTasks = (tasks) => {
    localStorage.setItem("tasks", JSON.stringify(tasks));
    forceUpdate();
  };


  return { addTasks, tasks }
};

const useForceUpdate = () => {
  const [_, setNum] = useState(0);

  const forceUpdate = () => setNum((prev) => ++prev);

  return { forceUpdate }
}
→ Ссылка