在reactjs中,是否可以创建一个不重置内容状态的选项卡控件?

问题描述

这是我的react hooks代码

function Panel({children,title}){
    var [count,set_count]=React.useState(0)
    React.useEffect(_=>{
        console.log(title,'mount')
        return _x=>console.log(title,'unmount')
    })
    function onClick(){
        set_count(count+10)
    }
    return <>{count}<button  {...{onClick}}>up</button><div>{children}</div></>
} 
function Tabs({children}){
    var [selected,set_selected]=React.useState(children[0].key)
    var tabs= children.map(x=>{
        function onClick(){
            set_selected(x.key)
        }
        var className=(selected==x.key)?'tab selected':'tab'
        return <div {...{className,onClick,key:x.key}}>{x.props.title||x.key}</div>
    })
    var panel=children.find(x=>x.key==selected)
    return <><div>{tabs}</div>{panel}</>
}
function TabsPage(){
    return <Tabs>
        <Panel key='the_tab1' title='The tab1 title'> tab1<b>rrr</b></Panel>
        <Panel key='the_tab2' title='The tab2 title'> tab2</Panel>
        </Tabs>
}

目标是使用react钩子创建具有任意内容的简单选项卡控件(称为面板)。该代码有效,除了每次用户更改活动选项卡时都可以安装/卸载面板组件。结果面板状态重置。

我的问题:更改标签时如何使状态不重置?不使用redux之类的全局状态存储,有可能吗?

解决方法

选项卡更改时状态会重置,因为每次更改选定的选项卡时,都会在“选项卡”组件中重新渲染面板变量。要保持状态,您有2个选项:

  • 将状态保留在TabsPage组件中,而不是redux,因此在卸载面板时不会丢失状态。
  • 始终显示所有选项卡,但仅显示选定的选项卡(使用显示或可见性之类的CSS属性)。这样,将不会卸载组件,因此您可以保持状态。

此外,即使与您的问题无关,我也要注意,在Panel组件中使用的useEffect代码不会跟踪安装/卸载状态。而是在每次渲染时触发它。如果您希望在安装和卸载时调用一次效果,则需要向效果添加一个依赖项数组。选中React documentation

,

这是一个相当误解,认为react钩子中的useEffect具有安装效果,而useEffect中的清理具有卸载效果(即使我在学习钩子时也曾想过)。

简而言之,每个渲染器都有其自己的状态,道具和效果。并在效果再次发生之前调用clean内部的clean函数。多数民众赞成在每种效果的依赖关系出现时,它们决定useEffect发生的时间。当前,您拥有的useEffect在每个渲染上都执行,因为它没有依赖性。

我强烈建议您阅读此博客post,以了解useEffect的行为。

解决此问题的方法是实现活动面板TabsPage并将selectedKey发送到Panel。如果键相同,则可以切换显示内容。

因此该组件看起来与此类似

function TabsPage() {
  const [selectedKey,setSelectedKey] = React.useState(the_tab1);
  const handlePanelClick = useCallback((id) => {
    setSelectedKey(id);
  },[]);
  return (
    <Tabs>
      <Panel
        key="the_tab1"
        title="The tab1 title"
        selectedKey={selectedKey}
        onClick={handlePanelClick}
      >
        tab1<b>rrr</b>
      </Panel>
      <Panel
        key="the_tab2"
        title="The tab2 title"
        selectedKey={selectedKey}
        onClick={handlePanelClick}
      >
        tab2
      </Panel>
    </Tabs>
  );
}

function Panel({ key,children,handlePanelClick,selectedKey }) {
  return (
    <>
      {/** to show hide the panel */}
      <button onClick={() => handlePanelClick(key)}>+</button>
      {key === selectedKey && <div>{children}</div>}
    </>
  );
}