You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React组件定时保存时noteTitle与noteContent长度始终为0问题

React自动保存功能状态无法更新的问题解决

问题原因

你的定时器在依赖为空数组的useEffect中创建,这个effect仅在组件挂载时执行一次。此时它捕获的是noteTitle和noteContent的初始空值,后续即使通过setNoteTitle/setNoteContent更新状态,定时器内的handleSave依然引用最初的空状态,导致始终无法触发保存逻辑。

解决方案

提供两种实用解决方式:

方式1:使用useRef跟踪最新状态

通过useRef存储实时更新的状态值,确保定时器能拿到最新数据:

import React, { useState, useEffect, useRef } from 'react';
import { useParams } from 'react-router-dom';
import axios from 'axios';
import qs from 'qs';

function ViewNoteComponent({ route }) {
    const { noteid } = useParams();
    const [noteTitle, setNoteTitle] = useState('');
    const [noteContent, setNoteContent] = useState('');
    // 创建ref保存最新状态
    const titleRef = useRef(noteTitle);
    const contentRef = useRef(noteContent);

    // 同步ref与状态变化
    useEffect(() => {
        titleRef.current = noteTitle;
        contentRef.current = noteContent;
    }, [noteTitle, noteContent]);

    useEffect(() => {
        let token = localStorage.getItem('token');
        fetch(`http://localhost:3000/list-single?token=${token}&note_id=${noteid}`)
            .then((response) => {
                if (response.ok) return response.json();
                throw new Error('Failed to fetch notes');
            })
            .then((data) => {
                setNoteTitle(data.data[0].title);
                setNoteContent(data.data[0].content);
            })
            .catch((error) => console.error('Error:', error.message));
    }, [noteid]);

    useEffect(() => {
        const timer = setInterval(() => {
            console.log("checking...")
            handleSave();
        }, 3000);

        return () => {
            clearInterval(timer);
            handleSave();
        };
    }, []);

    const handleSave = () => {
        console.log("saving...")
        // 从ref获取最新状态值
        const currentTitle = titleRef.current;
        const currentContent = contentRef.current;
        console.log(currentTitle.length)
        if (currentTitle.length > 0 && currentContent.length > 0) {
            let token = localStorage.getItem('token');
            let data = qs.stringify({ title: currentTitle, content: currentContent, token, note_id: noteid });

            console.log(data + 'sending');

            let config = {
                method: 'post',
                maxBodyLength: Infinity,
                url: 'http://localhost:3000/edit',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                data: data
            };

            axios
                .request(config)
                .then((response) => console.log(JSON.stringify(response.data)))
                .catch((error) => console.log(error));
        }
    };

    const handleTitleChange = (event) => {
        const newTitle = event.target.value;
        setNoteTitle(newTitle);
    };

    const handleContentChange = (event) => {
        const newContent = event.target.value;
        setNoteContent(newContent);
    };


    return (
        <>
            <div className='edit-area'>
                <input
                    className='input-note-title'
                    type='text'
                    onChange={handleTitleChange}
                    placeholder='Note title...'
                    value={noteTitle}
                />
                <textarea
                    className='input-note-content'
                    onChange={handleContentChange}
                    placeholder='Write something....'
                    value={noteContent}
                ></textarea>

                <button className='btn' >
                    Save
                </button>
            </div>
        </>
    );
}

export default ViewNoteComponent;

方式2:给定时器Effect添加依赖并使用useCallback

将handleSave用useCallback包裹,避免不必要的定时器重建,同时把它加入Effect依赖数组:

import React, { useState, useEffect, useCallback } from 'react';
import { useParams } from 'react-router-dom';
import axios from 'axios';
import qs from 'qs';

function ViewNoteComponent({ route }) {
    const { noteid } = useParams();
    const [noteTitle, setNoteTitle] = useState('');
    const [noteContent, setNoteContent] = useState('');

    useEffect(() => {
        let token = localStorage.getItem('token');
        fetch(`http://localhost:3000/list-single?token=${token}&note_id=${noteid}`)
            .then((response) => {
                if (response.ok) return response.json();
                throw new Error('Failed to fetch notes');
            })
            .then((data) => {
                setNoteTitle(data.data[0].title);
                setNoteContent(data.data[0].content);
            })
            .catch((error) => console.error('Error:', error.message));
    }, [noteid]);

    // 用useCallback包裹,仅在依赖变化时重新创建函数
    const handleSave = useCallback(() => {
        console.log("saving...")
        console.log(noteTitle.length)
        if (noteTitle.length > 0 && noteContent.length > 0) {
            let token = localStorage.getItem('token');
            let data = qs.stringify({ title: noteTitle, content: noteContent, token, note_id: noteid });

            console.log(data + 'sending');

            let config = {
                method: 'post',
                maxBodyLength: Infinity,
                url: 'http://localhost:3000/edit',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                data: data
            };

            axios
                .request(config)
                .then((response) => console.log(JSON.stringify(response.data)))
                .catch((error) => console.log(error));
        }
    }, [noteTitle, noteContent, noteid]);

    useEffect(() => {
        const timer = setInterval(() => {
            console.log("checking...")
            handleSave();
        }, 3000);

        return () => {
            clearInterval(timer);
            handleSave();
        };
    }, [handleSave]); // 添加handleSave作为依赖

    const handleTitleChange = (event) => {
        const newTitle = event.target.value;
        setNoteTitle(newTitle);
    };

    const handleContentChange = (event) => {
        const newContent = event.target.value;
        setNoteContent(newContent);
    };


    return (
        <>
            <div className='edit-area'>
                <input
                    className='input-note-title'
                    type='text'
                    onChange={handleTitleChange}
                    placeholder='Note title...'
                    value={noteTitle}
                />
                <textarea
                    className='input-note-content'
                    onChange={handleContentChange}
                    placeholder='Write something....'
                    value={noteContent}
                ></textarea>

                <button className='btn' >
                    Save
                </button>
            </div>
        </>
    );
}

export default ViewNoteComponent;

额外优化建议

  • 增加防抖逻辑:用户连续输入时,延迟到输入停止后再执行保存,避免频繁请求。
  • 添加自动保存提示:给用户短暂的视觉反馈(如“已自动保存”),提升交互体验。

内容的提问来源于stack exchange,提问作者Vikash Anand Jha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 02:32:41