Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: react18 submenu flicker #551

Merged
merged 1 commit into from
Nov 22, 2022

Conversation

JarvisArt
Copy link
Contributor

@JarvisArt JarvisArt commented Nov 15, 2022

修复 React18 中使用 createRoot 渲染 Submenu 会闪烁
ant-design/ant-design#38534

Edit on CodeSandbox

2022-11-16.23.30.37.mov

关键代码最小化:

const Menu = ({ mode, inlineCollapsed, defaultOpenKeys }) => {

  const [mergedOpenKeys, setMergedOpenKeys] = React.useState(defaultOpenKeys);
  const [mergedMode, mergedInlineCollapsed] = React.useMemo(() => {
    if ((mode === 'inline' || mode === 'vertical') && inlineCollapsed) {
      return ['vertical', inlineCollapsed];
    }
    return [mode, inlineCollapsed];
  }, [mode, inlineCollapsed]);

  React.useEffect(() => {
    if (mergedMode === 'inline') {
      setMergedOpenKeys([...]);
    } else {
      setMergedOpenKeys([]);
    }
  }, [mergedMode]);

  return (
    <MenuContextProvider
      mode={mergedMode}
      openKeys={mergedOpenKeys}
    >
      <Submenu />
    </MenuContextProvider>
  );
};

<Submenu key="sub_key" /> 的显示条件是 mode==="vertical" && openKeys.includes("sub_key")
点击 Collapsed(false->true) 时会发生以下渲染:

  1. 第一次渲染:mode="vertical"openKeys=["sub_key"],显示条件成立,出现 Submenu,mergedInlineCollapsed 的改变触发了 setMergedOpenKeys([])
  2. 第二次次渲染:mode="vertical"openKeys=[],显示条件不成立,隐藏 Submenu
  3. ...
  4. 所以才会造成 Submenu 的闪烁

为什么在 React17 不会出现闪烁:
只有 React18 才有 ReactDOM.createRoot 这个 api,使用该渲染 api 才会出现闪烁的问题,因为它有着并发的能力。经过调试也发现点击 Collapsed(false->true)React17React18 的两个渲染 api 的渲染间隔有所不同(多次调试取大概平均值)

API 第一次渲染 第二次渲染 第三次渲染 第四次渲染 第五次渲染
React17 0ms (true) 2ms (false) 3ms (false) 4ms (false) 5ms (false)
React18 0ms (true) 4ms (false) 8ms (false) 18ms (false) 24ms (false)

可以看出 React17 的渲染是不间断的、同步的事务,一旦渲染开始,就不能被中断,
而 React18 在并发模式下整个渲染时间变长了,因为在并发的能力可以在渲染的过程中去做一些其他的事情

根据上面的原因可以修改成以下的形式,把 setInternalMode、setInternalInlineCollapsed、setMergedOpenKeys 放在同一个地方赋值,这样利用到了 React 的批处理能力,让 mode 与 openKeys 同步修改。

const Menu = ({ mode, inlineCollapsed, defaultOpenKeys }) => {

  const [mergedOpenKeys, setMergedOpenKeys] = React.useState(defaultOpenKeys);
  const [mergedMode, mergedInlineCollapsed] = React.useMemo(() => {
    if ((mode === 'inline' || mode === 'vertical') && inlineCollapsed) {
      return ['vertical', inlineCollapsed];
    }
    return [mode, inlineCollapsed];
  }, [mode, inlineCollapsed]);

  const [internalMode, setInternalMode] = React.useState(mergedMode);
  const [internalInlineCollapsed, setInternalInlineCollapsed] = React.useState(mergedInlineCollapsed);

  React.useEffect(() => {
    setInternalMode(mergedMode);
    setInternalInlineCollapsed(mergedInlineCollapsed)
    
    if (mergedMode === 'inline') {
      setMergedOpenKeys([...]);
    } else {
      setMergedOpenKeys([]);
    }
  }, [mergedMode, mergedInlineCollapsed]);

  return (
    <MenuContextProvider
      mode={internalMode}
      openKeys={mergedOpenKeys}
    >
      <Submenu />
    </MenuContextProvider>
  );
};

@codecov
Copy link

codecov bot commented Nov 15, 2022

Codecov Report

Merging #551 (bd7ff3e) into master (2a2d956) will increase coverage by 0.00%.
The diff coverage is 100.00%.

❗ Current head bd7ff3e differs from pull request most recent head 2df968c. Consider uploading reports for the commit 2df968c to get more accurate results

@@           Coverage Diff           @@
##           master     #551   +/-   ##
=======================================
  Coverage   99.85%   99.85%           
=======================================
  Files          26       26           
  Lines         698      703    +5     
  Branches      188      191    +3     
=======================================
+ Hits          697      702    +5     
  Misses          1        1           
Impacted Files Coverage Δ
src/Menu.tsx 100.00% <100.00%> (ø)

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@afc163 afc163 requested a review from zombieJ November 16, 2022 02:10
@zombieJ zombieJ merged commit 1771fce into react-component:master Nov 22, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants