reactjs - useState 未按预期运行
问题描述
我对 useState 有疑问。从openEditorData设置时editorDataOpen正确更新,但从closeEditorData设置时不正确。到达console.log('Entering Profile > closeEditorData()')行没有问题。
我在控制台日志中看到的输出是:
- 使成为
- 使成为
- false(第一次设置editorDataOpen)
- 使成为
- 使成为
- 使成为
- 使成为
- true(editorDataOpen 通过单击设置为 true,打开编辑器)
- 进入 Profile > closeEditorData() (由不同的点击触发以关闭编辑器,应将 editorDataOpen 设置为 false,但不会)
- 使成为
- 使成为
就是这样,它永远不会打印最后一个false,这意味着editorDataOpen永远不会设置?
我今天在这上面花了太多时间,我只是看不到错误在哪里。有人可以帮忙吗?这是有问题的代码:
import React from 'react'
import {withTheme} from 'styled-components'
import {withContext} from '../../context/ContextHOC'
import withLoadingScreen from '../hocs/LoadingScreenHOC'
import {getEditorWidth} from '../../functions/funcEditor'
import {saveImagePlus} from '../../functions/funcDataSaver'
import Submodule from '../ui/Submodule'
import ImageCapsule from '../ui/ImageCapsule'
import EditorImage from '../editors/EditorImage'
import EditorProfileData from '../editors/EditorProfileData'
import Spacer from '../ui/Spacer'
import Table from '../ui/Table'
import ContainerGrid from '../ui/ContainerGrid'
import * as ops from '../../functions/funcStringMath'
import * as parse from '../../functions/funcDataParser'
const Profile = (props) => {
const s = props.theme.sizes
const c = props.theme.cards
const {setLoadingOn, setLoadingOff} = props
const [image, setImage] = React.useState(props.context.current.modules.me.profile.image)
const [editorImageOpen, setEditorImageOpen] = React.useState(false)
const [editorDataOpen, setEditorDataOpen] = React.useState(false)
const openEditorImage = () => setEditorImageOpen(true)
const openEditorData = () => setEditorDataOpen(true)
const closeEditorImage = () => {
setEditorImageOpen(false)
setLoadingOff()
}
const closeEditorData = () => {
console.log('Entering Profile > closeEditorData()')
setEditorDataOpen(false)
setLoadingOff()
}
React.useEffect(() => console.log(editorDataOpen), [editorDataOpen])
const updateAfterSavingImage = (img) => {
setImage({
url: img.url,
scale: img.scale,
position: img.position
})
closeEditorImage()
}
const handleImageChanged = (img) => {
if (img != undefined){
setLoadingOn()
const data = {
companyId: props.context.current.company.id,
userId: props.context.current.user.id,
routeFile: props.context.routes.meProfileImage,
routeData: props.context.routes.meProfileImageData,
}
saveImagePlus(img, data, updateAfterSavingImage)
}
else {
console.log('Error: Image received is undefined, cannot save.')
closeEditorImage()
}
}
const spacer =
<Spacer
width = '100%'
height = {s.spacing.default}
/>
const unparsedData = props.context.current.modules.me.profile.data
const parsedData = parse.profileData(props.context.current.modules.me.profile.data)
console.log('Render')
return(
<Submodule
isMobile = {c.cardsPerRow == 1 ? true : false}
header = {{
text: 'Profile',
}}
{...props}
>
<ImageCapsule
onClick = {openEditorImage}
id = {'container_imageprofile'}
width = '100%'
image = {image}
$nodrag
/>
{editorImageOpen &&
<EditorImage
open = {editorImageOpen}
closeSaving = {handleImageChanged}
closeWithoutSaving = {closeEditorImage}
image = {image}
width = {getEditorWidth(1, c.cardsPerRow, c.card.width, s.spacing.default)}
header = {{
text: 'Edit Profile Image',
}}
/>
}
{spacer}
{spacer}
<ContainerGrid
// bgcolor = '#C43939'
width = '100%'
justify = {s.justify.center}
onClick = {openEditorData}
$clickable
>
<Table
$nomouse
width = '100%'
data = {parsedData}
settings = {{
cell: {
padleft: s.spacing.default,
padright: s.spacing.default,
padtop: ops.round((ops.divide([s.spacing.default, 4]))),
padbottom: ops.round((ops.divide([s.spacing.default, 4]))),
},
columns: [
{type: 'defaultRight', width: '30%'},
{type: 'default', width: '70%'},
]
}}
/>
{editorDataOpen &&
<EditorProfileData
open = {editorDataOpen}
close = {closeEditorData}
width = {getEditorWidth(1, c.cardsPerRow, c.card.width, s.spacing.default)}
header = {{
text: 'Edit Profile Data',
}}
data = {unparsedData}
/>
}
</ContainerGrid>
{spacer}
{spacer}
</Submodule>
)
}
export default (
withTheme(
withContext(
withLoadingScreen(
Profile
)
)
)
)
编辑:这个问题已由 Dehan de Croos 解决,非常感谢!
因此,正如 Dehan 下面提到的,该事件正在冒泡并触发 ContainerGrid 中的 openEditorData。整个事情变得混乱,因为 editorImageOpen 工作正常,而 editorDataOpen 没有,而且他们都做同样的事情:打开一个编辑器窗口。Dehan 解决了这个问题后,我意识到两者之间的区别在于 ImageCapsule 内部有一个 ClickLayer 组件,它只是为了捕捉点击和回调。我没有使用带有 ContainerGrid 的 ClickLayer,这就是事件能够冒泡的原因。按照 Dehan 的建议,我只是通过在 ContainerGrid 中添加 ClickLayer 来解决,如下所示:
<ContainerGrid
// bgcolor = '#C43939'
width = '100%'
justify = {s.justify.center}
// onClick = {openEditorData}
$clickable
>
<ClickLayer
onClick = {openEditorData}
/>
<Table
$nomouse
width = '100%'
data = {parsedData}
settings = {{
cell: {
padleft: s.spacing.default,
padright: s.spacing.default,
padtop: ops.round((ops.divide([s.spacing.default, 4]))),
padbottom: ops.round((ops.divide([s.spacing.default, 4]))),
},
columns: [
{type: 'defaultRight', width: '30%'},
{type: 'default', width: '70%'},
]
}}
/>
{editorDataOpen &&
<EditorProfileData
open = {editorDataOpen}
close = {closeEditorData}
width = {getEditorWidth(1, c.cardsPerRow, c.card.width, s.spacing.default)}
header = {{
text: 'Edit Profile Data',
}}
data = {unparsedData}
/>
}
</ContainerGrid>
解决方案
最好有一个有效的代码片段来诊断这个问题,但是事件很有可能会在组件树中冒泡并在openEditorData
此处触发事件。
<ContainerGrid
// bgcolor = '#C43939'
width = '100%'
justify = {s.justify.center}
onClick = {openEditorData}
$clickable
>
要检查这一点,请快速将下图所示的组件移到组件外<ContainerGrid ... />
。并检查这是否可以解决问题。
在这里澄清一下,代码的移动应该发生,以便 < EditorProfileData ... /> 永远不会作为子组件出现
<ContainerGrid ... />
{editorDataOpen &&
<EditorProfileData
open = {editorDataOpen}
close = {closeEditorData}
width = {getEditorWidth(1, c.cardsPerRow, c.card.width, s.spacing.default)}
header = {{
text: 'Edit Profile Data',
}}
data = {unparsedData}
/>
}
如果现在它工作正常并且您迫切需要维护上述组件层次结构/结构。您可以调用
stopPropegation但您需要随身携带本机 JS 事件。为了解释如何做到这一点,我需要知道是什么<EditorProfileData ... />
样子的。但假设close
prop 确实将本机点击事件作为回调返回,修复将如下所示。
{editorDataOpen &&
<EditorProfileData
open = {editorDataOpen}
//close = {closeEditorData}
// If close provides the native event use following
close = { e => {
e.stopPropagation();
closeEditorData();
}}
width = {getEditorWidth(1, c.cardsPerRow, c.card.width, s.spacing.default)}
header = {{
text: 'Edit Profile Data',
}}
data = {unparsedData}
/>
}
如果不是,我们需要找到原始的 onClick 事件并传递给该道具回调。
推荐阅读
- c# - 如何使用 SDK4 从带有 Bot 的 MS Teams 获取用户电子邮件?
- pandas - 根据条件将备用标量列连接到熊猫
- java - 使用有效负载的 Restlet 删除请求
- javascript - 在 react.js 中调度动作时如何在 useEffect() 中使用 if/else
- three.js - Threejs:没有绑定到单元1的纹理
- r - 使用 gContains 并检查点是否在多边形/国家内。R
- python - 嵌入图像未在不和谐 python 中加载
- python - PyQGIS:通过多个表达式选择
- python - 仅使用不记名令牌的 Python gRPC 客户端请求
- python - 在 json 中传递 00 会产生错误的请求