Javascript React-使用受控输入将博客文章自动保存到sessionStorage()
我在尝试实现自动保存功能以捕获受控输入并将其保存到Javascript React-使用受控输入将博客文章自动保存到sessionStorage(),javascript,reactjs,Javascript,Reactjs,我在尝试实现自动保存功能以捕获受控输入并将其保存到sessionStorage()时遇到了一个奇怪的问题 我有一个自动保存功能,每30秒运行一次。我从输入值创建一个对象,然后将它们保存到sessionStorage()中。我运行一个检查,看看我创建的输入值对象是否与当前存储的对象匹配。如果新对象不同,我将用此新对象替换sessionStorage中的当前对象。我觉得这很直截了当 现在发生的事情是,我正在观察sessionStorage一次更新一个字符,就像我使用的受控输入在通过onChange(
sessionStorage()
时遇到了一个奇怪的问题
我有一个自动保存功能,每30秒运行一次。我从输入值创建一个对象,然后将它们保存到sessionStorage()
中。我运行一个检查,看看我创建的输入值对象是否与当前存储的对象匹配。如果新对象不同,我将用此新对象替换sessionStorage
中的当前对象。我觉得这很直截了当
现在发生的事情是,我正在观察sessionStorage一次更新一个字符,就像我使用的受控输入在通过onChange()
函数设置其值时的工作方式一样。一旦用我键入的内容完全更新对象,它将重置为空白
我将在代码示例下方的sessionStorage
中显示所述问题的示例
这是我的AddPost
组件,其中包含“添加帖子”表单和当前的自动保存功能:
import React, { useState, useEffect } from 'react';
//Styles
import {
AddPostContainer,
AddPostInfoInput,
AddPostInfoLabel,
AddPostTextArea,
PostOptionWrapper,
PostOptionGroup,
AddPostBtn
} from './styles';
//Components
import LivePreview from './LivePreview/LivePreview';
import Icon from '../../../Icons/Icon';
const AddPost = props => {
const [htmlString, setHtmlString] = useState('');
const [title, setTitle] = useState('');
const [postBody, setPostBody] = useState('');
const [author, setAuthor] = useState('');
const [tags, setTags] = useState('');
const [featuredImage, setFeaturedImage] = useState('');
const autoSave = async () => {
const autoSaveObject = {
title,
author,
tags,
featuredImage,
postBody
};
try {
await window.sessionStorage.setItem(
'add_post_auto_save',
JSON.stringify(autoSaveObject)
);
} catch (e) {
return;
}
};
setInterval(() => {
const currentSave = window.sessionStorage.getItem('add_post_auto_save');
const autoSaveObject = {
title,
author,
tags,
featuredImage,
postBody
};
if (currentSave === JSON.stringify(autoSaveObject)) {
return;
} else {
autoSave();
}
}, 10000);
return (
<AddPostContainer>
<AddPostInfoLabel htmlFor="title">Title</AddPostInfoLabel>
<AddPostInfoInput
type="text"
value={title}
onChange={e => setTitle(e.target.value)}
placeholder="enter post title"
id="title"
/>
<AddPostTextArea
inputwidth="100%"
height="400px"
value={postBody}
onChange={e => {
setHtmlString(e.target.value);
setPostBody(e.target.value);
}}
/>
<AddPostInfoLabel htmlFor="postbody">Live Preview:</AddPostInfoLabel>
<LivePreview id="postbody" htmlstring={htmlString} />
<PostOptionWrapper>
<PostOptionGroup width="33%">
<AddPostInfoLabel htmlFor="author">Author:</AddPostInfoLabel>
<AddPostInfoInput
type="text"
value={author}
onChange={e => setAuthor(e.target.value)}
placeholder="enter author's name"
id="author"
inputwidth="60%"
/>
</PostOptionGroup>
<PostOptionGroup width="33%">
<AddPostInfoLabel htmlFor="tags">Tags:</AddPostInfoLabel>
<AddPostInfoInput
type="text"
placeholder="enter tags separated by ,"
value={tags}
onChange={e => setTags(e.target.value)}
id="tags"
inputwidth="60%"
/>
</PostOptionGroup>
<PostOptionGroup width="33%">
<AddPostInfoLabel htmlFor="featuredImage">
Feat. Image:
</AddPostInfoLabel>
<AddPostInfoInput
type="text"
placeholder="enter image url"
value={featuredImage}
onChange={e => setFeaturedImage(e.target.value)}
id="featuredImage"
inputwidth="60%"
/>
</PostOptionGroup>
</PostOptionWrapper>
<AddPostBtn type="button">
<Icon icon={['far', 'plus-square']} size="lg" pSize="1em">
<p>Add Post</p>
</Icon>
</AddPostBtn>
</AddPostContainer>
);
};
export default AddPost;
此自动保存功能每30秒运行一次,然后将会话存储中的内容替换为输入字段的当前值。我在对象上使用JSON.stringify()
来比较它们。(注意:会话存储
中的obj已经字符串化。)。如果该匹配返回true,则不会保存任何内容,因为当前输入值也是保存的值。否则,它会将新对象保存到会话存储中
我的想法是,我需要使autoSave()
异步,因为更新会话和本地存储都是异步的,不会立即发生(尽管非常接近)。那没用
以下是sessionStorage
对象试图保存时的内容:
它的质量可能较低,但您可以看到它是如何更新“title”属性的。它的行为类似于受控输入,一个字符一个字符地添加到值中
有人能指出这是怎么回事吗?在这件事上我不知所措。提前谢谢 问得好,格式也不错。您的问题之所以会发生,是因为您每次更新组件时都会创建一个新的间隔,这是因为您使用的是受控输入。我想您可以将组件更改为类组件,并在
componentDidMount
方法上创建setInterval
。不要忘记清理组件卸载的间隔,下面是一个示例:
import ReactDOM from "react-dom";
import React from "react";
class Todo extends React.Component {
state = {
text1: "",
text2: "",
interval: null
};
componentDidMount() {
this.setState({
interval: setInterval(() => {
const { text1, text2 } = this.state;
const autoSaveObject = {
text1,
text2
};
console.log(JSON.stringify(autoSaveObject));
}, 3000)
});
}
componentWillUnmount() {
clearInterval(this.state.interval);
}
render() {
return (
<div>
<h1>TODO LIST</h1>
<form>
<input
value={this.state.text1}
onChange={e => this.setState({ text1: e.target.value })}
/>
<input
value={this.state.text2}
onChange={e => this.setState({ text2: e.target.value })}
/>
</form>
</div>
);
}
}
ReactDOM.render(<Todo />, document.getElementById("root"));
从“react-dom”导入ReactDOM;
从“React”导入React;
类Todo扩展了React.Component{
状态={
文本1:“,
文本2:“,
间隔:空
};
componentDidMount(){
这是我的国家({
间隔:设置间隔(()=>{
const{text1,text2}=this.state;
常数autoSaveObject={
文本1,
文本2
};
log(JSON.stringify(autoSaveObject));
}, 3000)
});
}
组件将卸载(){
clearInterval(this.state.interval);
}
render(){
返回(
待办事项清单
this.setState({text1:e.target.value})
/>
this.setState({text2:e.target.value})
/>
);
}
}
render(,document.getElementById(“根”));
您遇到的主要问题是setInterval
正在每个渲染中被调用,并且创建的间隔永远不会被清除
这意味着,如果在文本输入中键入10个字符,那么每10秒将触发10次间隔
为了避免这种情况,您需要使用钩子包装setInterval调用,并返回一个注销函数,该函数将在重新呈现(或卸载)时清除间隔。请参阅文档
以下是使用useffect
的最低更新版本:
const autoSave = (postData) => {
try {
window.sessionStorage.setItem(
'add_post_auto_save',
JSON.stringify(postData)
);
} catch (e) {
}
};
useEffect(() => {
const intervalId = setInterval(() => {
const autoSaveObject = {
title,
author,
tags,
featuredImage,
postBody
};
const currentSave = window.sessionStorage.getItem('add_post_auto_save');
if (currentSave === JSON.stringify(autoSaveObject)) {
return;
} else {
autoSave(autoSaveObject);
}
}, 10000);
return () => {clearInterval(intervalId)};
});
如果不希望在每次渲染时清除并重新创建间隔,则可以有条件地控制触发效果的时间。这在useEffect文档中有介绍。 最重要的是,您需要传入
useffect
的每一个依赖项,在您的情况下,它是所有状态变量
看起来是这样的-您需要确保包含useffect
钩子中使用的每个状态变量。如果您忘记列出任何变量,那么您将设置陈旧的数据
useEffect(() => {
//... your behaviour here
}, [title, author, tags, featuredImage, postBody]);
进一步阅读: 下面是Dan Abramov的一篇博文,以setInterval为例,深入探讨了钩子:
此外,如果post数据总是“捆绑”在一起是有意义的,那么您不需要有五个或六个单独的
useState
调用
您可以将post数据作为对象存储在useState
中,而不是单独管理它们:
const [postData, setPostData] = useState({
htmlString: '',
title: '',
author: '',
tags: '',
featuredImage: '',
postBody: '',
});
function updateData(value, key) {
setPostData((prevData) => {
return {
...prevData,
[key]: value
};
});
}
const autoSave = (postData) => {
try {
window.sessionStorage.setItem(
'add_post_auto_save',
JSON.stringify(postData)
);
} catch (e) {}
};
useEffect(() => {
const intervalId = setInterval(() => {
const currentSave = window.sessionStorage.getItem('add_post_auto_save');
if (currentSave === JSON.stringify(postData)) {
return;
} else {
autoSave(postData);
}
}, 10000);
return () => {
clearInterval(intervalId)
};
}, [postData]);
// jsx:
<AddPostInfoInput
type="text"
value={postData.title}
onChange={e => updateData(e.target.value, 'title')}
placeholder="enter post title"
id="title"
/>
const[postData,setPostData]=useState({
htmlString:“”,
标题:“”,
作者:'',
标签:“”,
特征图像:“”,
后正文:“”,
});
函数updateData(值、键){
setPostData((prevData)=>{
返回{
…以前的数据,
[键]:值
};
});
}
常量自动保存=(postData)=>{
试一试{
window.sessionStorage.setItem(
“添加后自动保存”,
JSON.stringify(postData)
);
}捕获(e){}
};
useffect(()=>{
const intervalId=setInterval(()=>{
const currentSave=window.sessionStorage.getItem('add\u post\u auto\u save');
if(currentSave==JSON.stringify(postData)){
返回;
}否则{
自动保存(postData);
}
}, 10000);
const [postData, setPostData] = useState({
htmlString: '',
title: '',
author: '',
tags: '',
featuredImage: '',
postBody: '',
});
function updateData(value, key) {
setPostData((prevData) => {
return {
...prevData,
[key]: value
};
});
}
const autoSave = (postData) => {
try {
window.sessionStorage.setItem(
'add_post_auto_save',
JSON.stringify(postData)
);
} catch (e) {}
};
useEffect(() => {
const intervalId = setInterval(() => {
const currentSave = window.sessionStorage.getItem('add_post_auto_save');
if (currentSave === JSON.stringify(postData)) {
return;
} else {
autoSave(postData);
}
}, 10000);
return () => {
clearInterval(intervalId)
};
}, [postData]);
// jsx:
<AddPostInfoInput
type="text"
value={postData.title}
onChange={e => updateData(e.target.value, 'title')}
placeholder="enter post title"
id="title"
/>
const [state,setState]=useState({
htmlString:"",
title:"",
postBody:"",
author:"",
tags:"",
featuredImage:""
})
useEffect(async () => {
const autoSaveObject = { ...state };
try {
await window.sessionStorage.setItem(
'add_post_auto_save',
JSON.stringify(autoSaveObject));
} catch (e) {
return console.log(e)
}
}, [state])