React Context API 很慢

Posted

技术标签:

【中文标题】React Context API 很慢【英文标题】:React Context API is slow 【发布时间】:2019-06-07 21:55:56 【问题描述】:

我正在试验新的 Context API 和钩子。我创建了一个带有侧边栏(树视图)、页脚和主要内容页面的应用程序。我有一个上下文提供者

const ContextProvider: FunctionComponent = (props) => 

const [selected, setSelected] = useState(undefined);
const [treeNodes, setTreeNodes] = useState([]);

return (
    <MyContext.Provider
        value=
            actions: 
                setSelected,
                setTreeNodes
            ,
            selected,
            treeNodes
        
    >
        props.children
    </MyContext.Provider>
);

作为我的内容组件,我有一个包含大约 1000 个项目的 DetailsList(Office Fabric UI)。当我单击列表中的项目时,我想在上下文中更新所选项目。这有效,但它真的很慢。选择列表中的项目大约需要 0,5-1 秒。该列表是虚拟化的。我已经在生产版本中尝试过。事情要好一些,但点击列表时有明显的滞后。 Footer 正在使用 myContext 来显示有关所选项目的信息。

这是我的组件中的一些代码

const cntx = useContext(MyContext);

const onClick = (item) => 
    cntx.actions.setSelected(item);
;

我是否使用了错误的上下文?

我创建了一个示例沙箱来演示。您可以滚动到大约第 100 个索引并单击几次以查看它是如何变得无响应的。

https://codesandbox.io/s/0m4nqxp4m0

这是 Fabric DetailsList 的问题吗?它会重新渲染多次吗?我相信问题出在“复杂”的 DatePicker 组件上,但我不明白为什么会重新渲染 DetailsList?它没有在渲染函数中使用任何上下文属性。我希望只有 Footer 组件在每次上下文更改时重新呈现

【问题讨论】:

控制台中有很多 "The icon "calendar" was used but not registered. See http://aka.ms/fabric-icon-usage for more information. " 警告 - 我担心仅此日志记录会导致问题 - 请检查 :) #edit - codesandbox.io/embed/llj1zk19rz 【参考方案1】:

使用 useMemo 记住您选择的项目,以避免在引用同一项目时创建新对象。然后在专用上下文中传递它

【讨论】:

【参考方案2】:

在您的解决方案中,每当您的组件被重新渲染时,value 的新实例将作为道具传递下来。这也会触发子元素的重新渲染。

如果您使用钩子,为了防止这种情况,请记住您要传递的 value 对象,在 useMemouseCallback 钩子中。事实上,这应该作为基本的 React 实践应用于任何组件,而不仅仅是上下文组件。除非您传递原始值(字符串、数字、...),否则不要直接创建实例或内联函数并将其作为道具传递。确保你传递的 props 在 React 组件的每个渲染周期后都不会改变。

【讨论】:

【参考方案3】:

注意事项 因为上下文使用引用标识来确定何时重新渲染,所以当提供者的父级重新渲染时,有一些陷阱可能会触发消费者的无意渲染。例如,下面的代码将在每次 Provider 重新渲染时重新渲染所有消费者,因为总是为 value 创建一个新对象:

class App extends React.Component 
  render() 
    return (
      <Provider value=something: 'something'>
        <Toolbar />
      </Provider>
    );
  

To get around this, lift the value into the parent’s state:

class App extends React.Component 
  constructor(props) 
    super(props);
    this.state = 
      value: something: 'something',
    ;
  

  render() 
    return (
      <Provider value=this.state.value>
        <Toolbar />
      </Provider>
    );
  

https://reactjs.org/docs/context.html#caveats

【讨论】:

以上是关于React Context API 很慢的主要内容,如果未能解决你的问题,请参考以下文章

在 react-native@0.55 发布版本中获取 API 很慢

如何创建使用 Typescript 传递函数的 React Context

React开发(206):react代码分割之context的api

React - 新的 Context API 不适用于 Class.contextType,但适用于 Context.Consumer

[React] Use the new React Context API

React API