React组件重渲染后保持下拉列表展开状态的方案咨询
解决方案:保持删除后子列表的展开状态
这个问题的核心原因很明确:listarProdutosItens组件的openProd状态是组件内部的局部状态,当你删除数据后调用fetchLista重新拉取数据,父组件AdmControls的tipos状态更新,会触发listarProdutos重新渲染,进而导致所有listarProdutosItens组件重新初始化——它们的useState(false)会再次执行,所以被删除项所在的子列表就会收起。
要解决这个问题,我们需要把子列表的展开状态提升到更上层的组件(比如listarProdutos),让状态脱离单个listarProdutosItens的生命周期,这样即使子组件重新渲染,状态也能被保留。
具体修改步骤
1. 修改listarProdutos组件,管理所有子列表的展开状态
把原来每个listarProdutosItens的openProd状态,改成由父组件用一个对象来管理,键是子列表的唯一标识(这里用子列表的标题itemTitulo就很合适),值是布尔值表示是否展开。
const listarProdutos = props => { const [open, setOpen] = React.useState(false); // 新增:管理所有子列表的展开状态,初始为空对象 const [openProdStates, setOpenProdStates] = React.useState({}); // 新增:切换指定子列表的展开状态 const toggleOpenProd = (titulo) => { setOpenProdStates(prev => ({ ...prev, [titulo]: !prev[titulo] })); }; return ( <div> <h1 style={{color: 'red'}} onClick={() => setOpen(!open)} >LISTA</h1> { open ? props.produtosLista.map((item, i) => ( <ListarProdutosItens listaItens={item} key={item+i} fetchLista={props.fetchLista} // 传递当前子列表的展开状态和切换函数 isOpen={openProdStates[item[0].titulo] || false} toggleOpen={() => toggleOpenProd(item[0].titulo)} /> )) : null} </div> ) }
2. 修改listarProdutosItens组件,使用父组件传递的状态
去掉组件内部的useState,改用父组件传入的isOpen和toggleOpen来控制展开状态:
const listarProdutosItens = props => { // 移除原来的局部状态:const [openProd, setOpenProd] = React.useState(false); let itemTitulo = props.listaItens[0].titulo; const excluirItemHandler = i => { let key = props.listaItens[i].key; let categoria = props.listaItens[i].categoria; axios.delete(`/adm/${categoria}/${key}.json`) .then(res => props.fetchLista()) .catch(error => console.log(error)) } return ( <div> {/* 改用父组件的toggleOpen函数 */} <h2 onClick={props.toggleOpen} >{itemTitulo}</h2> {/* 改用父组件传递的isOpen状态 */} {props.isOpen ? <table> <thead> <tr> <th>Nome</th> <th>Valor</th> { itemTitulo === 'Tipo de Acabamento Pia' || itemTitulo === 'Tipo de Rodopia' || itemTitulo === 'Tipo de Acabamento Seco' ? <th>Altura</th> : null } {itemTitulo === 'Tipo de Cuba' ? <th>Furo</th> : null} </tr> </thead> {props.listaItens.map((it, i) => { return ( i !== 0 ? <thead key={it+i}> <tr> <td>{it.nome}</td> <td>{it.valor}</td> { itemTitulo === 'Tipo de Acabamento Pia' || itemTitulo === 'Tipo de Rodopia' || itemTitulo === 'Tipo de Acabamento Seco' ? <td>{it.altura}</td> : null } {itemTitulo === 'Tipo de Cuba' ? <td>{it.furo}</td> : null} <td><button onClick={() => excluirItemHandler(i)} >Excluir</button></td> </tr> </thead> : null ) })} </table> : null} </div> ) }
为什么这样修改有效?
- 子列表的展开状态现在由
listarProdutos组件管理,而不是每个listarProdutosItens自己持有。即使删除数据后重新拉取,listarProdutos的openProdStates状态不会被重置,只有当用户手动点击切换时才会改变。 - 每个子列表通过唯一的标题
itemTitulo来绑定自己的展开状态,确保多个子列表的状态不会互相干扰。 - 这种方式符合React的状态提升原则:当多个组件需要共享状态或者状态需要在组件重新渲染时保留,就把状态提升到它们的共同父组件中。
内容的提问来源于stack exchange,提问作者Berg_Durden
相关产品推荐
相关产品推荐

