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

React MUI TextField使用sx正常但styled封装后异常原因

问题原因

你遇到的输入失焦、样式错乱问题,核心原因是将styled封装的自定义组件定义在了组件渲染函数内部:

  • 每次输入内容触发textValue状态更新时,组件会重渲染,渲染函数内部会重新生成一个引用完全不同的ModalTextField组件
  • React对比虚拟DOM时,会判定这是一个全新的组件类型,直接卸载旧的输入框组件、挂载新的组件,直接导致输入框焦点丢失,同时样式重新计算触发渲染错乱
  • 你在组件内部定义的ClearButton、CheckButton也存在同样的隐患,只是当前触发问题的核心是styled生成的输入框组件。
  • 之前使用styled没遇到问题,是因为之前的写法大概率是把styled组件定义在组件外部,不会随重渲染重新生成。

sx属性写法正常的原因是:sx是挂载在现有组件实例上的样式配置,不会生成新的组件类型,重渲染时React只会更新现有组件的属性,不会触发卸载重挂载流程。

修复方案
  1. 所有通过styled生成的自定义组件、以及独立渲染的子组件,全部移到当前组件函数的外部定义,保证组件引用在整个生命周期内稳定,不会随重渲染变化。
  2. 如果styled组件需要用到动态值(比如当前代码里依赖nameRect计算宽高),通过props给styled组件传参,不要在组件内部闭包读取动态值。
修复后完整代码
import React, { useState } from 'react';
import { 
    Box,
    IconButton,
    Modal,
    TextField
} from '@mui/material';
import { styled } from '@mui/material/styles';
import CheckIcon from '@mui/icons-material/Check'
import ClearIcon from '@mui/icons-material/Clear'

// styled组件移到外部定义,通过props接收动态配置
const ModalTextField = styled(TextField)(({ sizeConfig }) => ({
    backgroundColor: 'white',
    width: `calc(${sizeConfig.width}px - 2px)`,
    height: `calc(${sizeConfig.height}px - 2px)`,
    '& .MuiOutlinedInput-root': {
        borderRadius: '0rem',
        height: 'inherit',
        paddingRight: '0.25rem',
        fontSize: '0.875rem',
    }
}));

// 内部按钮组件也移到外部,通过props传入点击回调
const ClearButton = ({ onClick }) => (
    <IconButton onClick={onClick} style={{ color: 'red'}}>
        <ClearIcon />
    </IconButton>
);

const CheckButton = ({ onClick }) => (
    <IconButton onClick={onClick} style={{ color: 'green'}}>
        <CheckIcon />
    </IconButton>
);

export default function TextFieldModal(props) {
    const {
        nameRect,
        initialValue,
        handleModalClose
    } = props;
    const [openModal, setOpenModal] = useState(true);
    const [textValue, setTextValue] = useState(initialValue);

    const cancelEdit = () => {
        closeModal(null);
    };

    const saveNewName = () => {
        closeModal(textValue);
    };

    const closeModal = (newName) => {
        setOpenModal(false);
        console.log('closing value = ', textValue);
        handleModalClose(newName);
    };

    return (
        <Modal open={openModal}>
            <Box sx={{
                position: 'absolute',
                top: `${nameRect.top}px`,
                left: `${nameRect.left}px`,
                width: `calc(${nameRect.width}px - 2px)`,
                height: `calc(${nameRect.height}px - 2px)`
            }}>
                <ModalTextField
                    id='modal-textfield'
                    value={textValue}
                    onChange={(event) => setTextValue(event.target.value)}
                    sizeConfig={nameRect}
                    InputProps={{
                        endAdornment:
                            <>
                                <CheckButton onClick={saveNewName} />
                                <ClearButton onClick={cancelEdit} />
                            </>
                    }}
                />
            </Box>
        </Modal>
    );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 23:30:52