React类组件转Hooks后切换nextPackage可见性按钮失效如何解决
问题根因
你遇到的问题确实是状态对象处理错误导致,核心存在两处错误:
handleShowNextPackageDetails方法直接将整个状态对象替换为布尔值:你定义的initialState是包含多个业务属性的对象,直接执行setInitialState(!showNextPackage)会把状态整体改成true/false,原有pricelist、nextPackage、showNextPackage等属性全部丢失,自然无法控制折叠面板的展示。- 未注意
useState更新逻辑和类组件setState的差异:类组件的setState会自动做状态的浅层合并,但useState的更新函数是直接替换整个状态,你在useEffect中调用setInitialState时仅传入了三个属性,会直接把初始状态中定义的showNextPackage属性清除,状态结构从初始化阶段就已经不符合预期。
修复方案
方案1:保持单状态对象的修复方式
如果不想调整原有状态结构,只需要修正两处状态更新的逻辑,更新时保留原有状态的其他属性即可:
- 修正
handleShowNextPackageDetails方法:
const handleShowNextPackageDetails = () => { setInitialState(prevState => ({ ...prevState, showNextPackage: !prevState.showNextPackage })); };
- 修正
useEffect中的状态更新逻辑:
useEffect(() => { const { getData } = props; getData( 'price-lists/profile', { defaultSort, resourceArrayName: 'pricelist' }, true ).then((res) => { setInitialState(prevState => ({ ...prevState, pricelist: res.data._embedded.pricelist[0], nextPackage: res.data._embedded.pricelist[1], loading: false, })); }); // 建议将getData加入依赖数组,避免闭包问题 }, [getData]);
方案2:更合理的拆分状态方案(推荐)
对于关联性较弱的状态,拆分为多个独立的useState管理是更符合Hooks设计理念的实现,后续维护和更新不需要额外处理对象合并,出错概率更低:
const Pricing = props => { const [loading, setLoading] = useState(true); const [pricelist, setPricelist] = useState({ pricelistEntries: [], name: '', monthlySubscriptionFee: '', currency: '', validFrom: '', smsInPackage: '', }); const [nextPackage, setNextPackage] = useState(undefined); const [showNextPackage, setShowNextPackage] = useState(false); useEffect(() => { const { getData } = props; getData( 'price-lists/profile', { defaultSort, resourceArrayName: 'pricelist' }, true ).then((res) => { setPricelist(res.data._embedded.pricelist[0]); setNextPackage(res.data._embedded.pricelist[1]); setLoading(false); }); }, [getData]); const handleShowNextPackageDetails = () => { setShowNextPackage(prev => !prev); }; const { classes } = props; return ( <React.Fragment> <Paper className={classes.root}> <Loading loading={loading} spinner> <React.Fragment> <Typography variant="body1" style={{ marginBottom: 10 }}> <FormattedMessage id="administration.packages.form.section.package.info" /> </Typography> <PackageView pricelist={pricelist} /> </React.Fragment> </Loading> </Paper> {nextPackage && ( <Paper className={classes.next}> <div style={{ display: 'flex' }}> <Typography variant="body1" style={{ flexGrow: 1 }}> <FormattedMessage id="account.packages.form.section.next.package" /> </Typography> <Button color="secondary" onClick={handleShowNextPackageDetails} size="small" style={{ marginBottom: '5px' }} > <FormattedMessage id="account.balance.details.label" /> <ExpandMoreIcon className={classnames(classes.expand, { [classes.expandOpen]: showNextPackage, })} /> </Button> </div> <Collapse in={showNextPackage} timeout="auto" unmountOnExit> <PackageView pricelist={nextPackage} /> </Collapse> </Paper> )} </React.Fragment> ); }
内容的提问来源于stack exchange,提问作者Ati
相关产品推荐
相关产品推荐

