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

React函数式子组件Props变更时状态未更新问题排查

问题与修复方案

问题描述

我有一个集成弹窗子组件的表单模板,弹窗用于展示登录、找回密码等AJAX请求的服务器响应,且会在一段时间后自动消失。首次触发请求时弹窗正常显示响应信息并自动消失,但在同一页面再次触发请求(AJAX无刷新)时,消息已更新但弹窗不再显示。排查后怀疑子组件的isShown状态未随消息更新同步变化,但无法解决,可能存在其他遗漏问题。

相关代码

AppForm.jsx(父模板)

function MainContent(props) {
    const { title, underTitle, children, buttonText, customHandler, bottomPart } = props;
    const [banner, setBanner] = useState({code: null, msg: null})
    
    const reqHandler = async () => {
        customHandler().then((resp) => {
            setBanner({code: 'success', msg: resp.data.msg})
        }).catch((err) => {
            setBanner({ code: 'error', msg: err.response.data.msg })
        });
    }

    return (
        <ThemeProvider theme={formTheme}>
            <Container maxWidth="sm">
                <AppFormPopup statusCode={banner.code} msg={banner.msg} />
                <Box sx={{ mt: 7, mb: 12 }}>
                    <Paper>
                        {children} {/* 渲染派生表单页面所需的字段 */}

                        <Button type='submit' sx={{ mt: 3, mb: 2 }} onClick={reqHandler}>
                            {buttonText}
                        </Button>
                    </Paper>
                </Box>
            </Container>
        </ThemeProvider>
    )
}

function AppForm(props) {
    return ( 
       <>
         <AppFormNav /> 
         <MainContent {...props} />
       </>
    );
}

AppForm.propTypes = {
    children: PropTypes.node,
};

export default AppForm;

AppFormPopup.jsx(弹窗子组件)

function MainContent(props){
    const { msg } = props;

    const [progress, setProgress] = useState(0);
    const [isShown, setIsShown] = useState(true); // 控制弹窗可见性的状态

    useEffect(() => {
        const timer = setInterval(() => {
            setProgress((oldProgress) => {
                if(oldProgress === 100){
                    setIsShown(false);
                    // return 0;
                }
                return Math.min(oldProgress + 10, 100);
            });
        }, 500);

        return () => {
            clearInterval(timer);
        };
    });

    return (
        <Grid container sx={{
            display: isShown ? 'block' : 'none',
        }}>
            <Grid item sx={{ color: "white", pl: 1 }}> {msg} </Grid>
        </Grid>
    )
}

export default function AppFormPopup({msg}) {
    if(msg === null) return;
    return (
        <MainContent msg={msg} />
    )
} 

ForgotPass.jsx(继承表单模板的页面)

export default function ForgotPass() {
    const loc = useLocation().pathname;
    const mailRef = useRef(null);
    const reqHandler = async () => {
        const email = mailRef.current.value;
        if(email){
            const resp = await axios.post(loc, {email})
            return resp;
        }
    }

    return (
        <AppForm
            buttonText="Send Reset Link"
            customHandler={reqHandler}
        >
            <Box sx={{ mt: 6 }}>
                <TextField
                    autoFocus
                    label="Email"
                    inputRef={mailRef}
                />
            </Box>

        </AppForm>
    )
}

问题根源

  1. useEffect无依赖项:子组件MainContent中的useEffect没有设置依赖数组,会在每次组件渲染时重复执行,但首次执行后progress会走到100并将isShown设为false,后续消息更新时,useEffect触发后会直接检测到progress为100,再次将isShown设为false,导致弹窗无法显示。
  2. 状态未重置:当新的msg传入时,子组件没有重置isShown和progress的初始状态,弹窗仍保持之前的隐藏状态。

修复后的代码(核心修改在AppFormPopup.jsx)

function MainContent(props){
    const { msg } = props;

    const [progress, setProgress] = useState(0);
    const [isShown, setIsShown] = useState(true);

    // 当msg变化时,重置弹窗状态并启动定时器
    useEffect(() => {
        // 重置状态
        setIsShown(true);
        setProgress(0);
        
        const timer = setInterval(() => {
            setProgress((oldProgress) => {
                const newProgress = Math.min(oldProgress + 10, 100);
                if(newProgress === 100){
                    setIsShown(false);
                }
                return newProgress;
            });
        }, 500);

        return () => {
            clearInterval(timer);
        };
    }, [msg]); // 添加msg作为依赖项,仅在msg变化时执行

    return (
        <Grid container sx={{
            display: isShown ? 'block' : 'none',
        }}>
            <Grid item sx={{ color: "white", pl: 1 }}> {msg} </Grid>
        </Grid>
    )
}

export default function AppFormPopup({msg}) {
    if(msg === null) return null;
    return (
        <MainContent msg={msg} />
    )
} 

额外优化建议

  • 可以给弹窗添加状态对应的样式(比如成功用绿色背景、错误用红色背景),利用传入的statusCode实现。
  • 可以将自动消失的时间做成可配置的props,提升组件复用性。

内容的提问来源于stack exchange,提问作者Fallen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 12:25:32