首页 > 解决方案 > useState 未按预期运行

问题描述

我对 useState 有疑问。从openEditorData设置时editorDataOpen正确更新,但从closeEditorData设置时不正确。到达console.log('Entering Profile > closeEditorData()')行没有问题。

我在控制台日志中看到的输出是:

就是这样,它永远不会打印最后一个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>

标签: reactjsuse-state

解决方案


最好有一个有效的代码片段来诊断这个问题,但是事件很有可能会在组件树中冒泡并在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 ... />样子的。但假设closeprop 确实将本机点击事件作为回调返回,修复将如下所示。

            {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 事件并传递给该道具回调。


推荐阅读