在 Electron 应用程序中使用复杂状态对象反应 useState 问题
React useState issue with complex state object in Electron app
我正在使用 React 和 Electron 以及出色的 Slate.js 文本编辑器组件构建一个文本编辑器应用程序。我在管理状态时遇到问题。
代码
这里是完整的主要应用组件:
import React, { useState, useMemo, useEffect } from 'react'
import { createEditor } from 'slate'
import { Slate, Editable, withReact } from 'slate-react'
import TopBar from './TopBar'
const { ipcRenderer } = require('electron')
export const createSlateValue = (text) => {
const lines = text.split('\n')
return lines.map(line => (
{
children: [{ text: line }]
}
))
}
const getSlateValue = (editorValue) => editorValue.map(line => line.children[0].text).join('\n')
const Editor = () => {
const [currentDocument, setCurrentDocument] = useState()
const [isDoingFileOperations, setIsDoingFileOperations] = useState(false)
const [editorValue, setEditorValue] = useState(createSlateValue(''))
const editor = useMemo(() => withReact(createEditor()), [])
const onLoadDocument = async (doc) => {
setCurrentDocument(doc)
// Load file from disk
if (doc.path) {
try {
const text = await fsReadFileAsync(doc.path)
setEditorValue(createSlateValue(text))
} catch (error) {
window.alert('An error ocurred reading the file :' + error.message)
}
} else {
setEditorValue(createSlateValue(doc.name))
}
}
const onSaveCurrentDocument = async () => {
if (isDoingFileOperations) return
setIsDoingFileOperations(true)
try {
if (!(currentDocument && currentDocument.path)) throw new Error('No file path is set')
const text = getSlateValue(editorValue)
// await fsWriteFileAsync(currentDocument.path, text)
console.log('Text 2:', JSON.stringify(editorValue, null, 2))
} catch (error) {
window.alert('An error ocurred saving the file :' + error.message)
} finally {
setIsDoingFileOperations(false)
}
}
const handleTextChange = (val) => {
setEditorValue(val)
}
const requestDocument = doc => {
ipcRenderer.send('request-document', doc)
}
useEffect(() => {
const keyboardCommandListener = (event, cmd) => {
if (cmd === 'save') onSaveCurrentDocument()
}
ipcRenderer.on('keyboard-command', keyboardCommandListener)
const requestDocumentListener = (event, doc) => onLoadDocument(doc)
ipcRenderer.on('request-document-results', requestDocumentListener)
return () => {
ipcRenderer.removeListener('keyboard-command', keyboardCommandListener)
ipcRenderer.removeListener('request-document-results', requestDocumentListener)
}
}, [currentDocument])
console.log('Text 1:', JSON.stringify(editorValue, null, 2))
return (
<div>
<TopBar
currentDocument={currentDocument}
onLoadDocument={onLoadDocument}
onSaveDocument={onSaveCurrentDocument}
/>
<Slate
editor={editor}
value={editorValue}
onChange={handleTextChange}
>
<Editable
placeholder='Write here...'
onKeyDown={handleKeyDown}
/>
</Slate>
</div>
)
}
export default Editor
问题
当我将文本文档加载到编辑器中时,"Text 1" 日志正确显示,例如:
Text 1: [
{
"children": [
{
"text": "# Quadcopter with wheels"
}
]
},
{
"children": [
{
"text": ""
}
]
},
{
"children": [
{
"text": ""
}
]
}
]
但是当我使用 onSaveCurrentDocument
函数保存时,Text 2 日志语句显示空状态:
Text 2: [
{
"children": [
{
"text": ""
}
]
}
]
如果我将第二个文档加载到编辑器中,当我保存文本 2 时,将显示第一个文档的内容。 onSaveCurrentDocument
状态总是落后一步
可能是什么问题?
您的 useEffect 缺少一些依赖项:
useEffect(() => {
const keyboardCommandListener = (event, cmd) => {
if (cmd === 'save') onSaveCurrentDocument()
}
ipcRenderer.on('keyboard-command', keyboardCommandListener)
const requestDocumentListener = (event, doc) => onLoadDocument(doc)
ipcRenderer.on('request-document-results', requestDocumentListener)
return () => {
ipcRenderer.removeListener('keyboard-command', keyboardCommandListener)
ipcRenderer.removeListener('request-document-results', requestDocumentListener)
}
}, [currentDocument, onSaveCurrentDocument, onLoadDocument])
// onSaveCurrentDocument, onLoadDocument were missing
你能尝试围绕这个钩子建立你的登录吗?我推荐你使用 eslint hooks checks
我正在使用 React 和 Electron 以及出色的 Slate.js 文本编辑器组件构建一个文本编辑器应用程序。我在管理状态时遇到问题。
代码
这里是完整的主要应用组件:
import React, { useState, useMemo, useEffect } from 'react'
import { createEditor } from 'slate'
import { Slate, Editable, withReact } from 'slate-react'
import TopBar from './TopBar'
const { ipcRenderer } = require('electron')
export const createSlateValue = (text) => {
const lines = text.split('\n')
return lines.map(line => (
{
children: [{ text: line }]
}
))
}
const getSlateValue = (editorValue) => editorValue.map(line => line.children[0].text).join('\n')
const Editor = () => {
const [currentDocument, setCurrentDocument] = useState()
const [isDoingFileOperations, setIsDoingFileOperations] = useState(false)
const [editorValue, setEditorValue] = useState(createSlateValue(''))
const editor = useMemo(() => withReact(createEditor()), [])
const onLoadDocument = async (doc) => {
setCurrentDocument(doc)
// Load file from disk
if (doc.path) {
try {
const text = await fsReadFileAsync(doc.path)
setEditorValue(createSlateValue(text))
} catch (error) {
window.alert('An error ocurred reading the file :' + error.message)
}
} else {
setEditorValue(createSlateValue(doc.name))
}
}
const onSaveCurrentDocument = async () => {
if (isDoingFileOperations) return
setIsDoingFileOperations(true)
try {
if (!(currentDocument && currentDocument.path)) throw new Error('No file path is set')
const text = getSlateValue(editorValue)
// await fsWriteFileAsync(currentDocument.path, text)
console.log('Text 2:', JSON.stringify(editorValue, null, 2))
} catch (error) {
window.alert('An error ocurred saving the file :' + error.message)
} finally {
setIsDoingFileOperations(false)
}
}
const handleTextChange = (val) => {
setEditorValue(val)
}
const requestDocument = doc => {
ipcRenderer.send('request-document', doc)
}
useEffect(() => {
const keyboardCommandListener = (event, cmd) => {
if (cmd === 'save') onSaveCurrentDocument()
}
ipcRenderer.on('keyboard-command', keyboardCommandListener)
const requestDocumentListener = (event, doc) => onLoadDocument(doc)
ipcRenderer.on('request-document-results', requestDocumentListener)
return () => {
ipcRenderer.removeListener('keyboard-command', keyboardCommandListener)
ipcRenderer.removeListener('request-document-results', requestDocumentListener)
}
}, [currentDocument])
console.log('Text 1:', JSON.stringify(editorValue, null, 2))
return (
<div>
<TopBar
currentDocument={currentDocument}
onLoadDocument={onLoadDocument}
onSaveDocument={onSaveCurrentDocument}
/>
<Slate
editor={editor}
value={editorValue}
onChange={handleTextChange}
>
<Editable
placeholder='Write here...'
onKeyDown={handleKeyDown}
/>
</Slate>
</div>
)
}
export default Editor
问题
当我将文本文档加载到编辑器中时,"Text 1" 日志正确显示,例如:
Text 1: [
{
"children": [
{
"text": "# Quadcopter with wheels"
}
]
},
{
"children": [
{
"text": ""
}
]
},
{
"children": [
{
"text": ""
}
]
}
]
但是当我使用 onSaveCurrentDocument
函数保存时,Text 2 日志语句显示空状态:
Text 2: [
{
"children": [
{
"text": ""
}
]
}
]
如果我将第二个文档加载到编辑器中,当我保存文本 2 时,将显示第一个文档的内容。 onSaveCurrentDocument
状态总是落后一步
可能是什么问题?
您的 useEffect 缺少一些依赖项:
useEffect(() => {
const keyboardCommandListener = (event, cmd) => {
if (cmd === 'save') onSaveCurrentDocument()
}
ipcRenderer.on('keyboard-command', keyboardCommandListener)
const requestDocumentListener = (event, doc) => onLoadDocument(doc)
ipcRenderer.on('request-document-results', requestDocumentListener)
return () => {
ipcRenderer.removeListener('keyboard-command', keyboardCommandListener)
ipcRenderer.removeListener('request-document-results', requestDocumentListener)
}
}, [currentDocument, onSaveCurrentDocument, onLoadDocument])
// onSaveCurrentDocument, onLoadDocument were missing
你能尝试围绕这个钩子建立你的登录吗?我推荐你使用 eslint hooks checks