轻松渲染优化:使用React Hooks进行state跟踪

轻松渲染优化:使用React Hooks进行state跟踪

轻松渲染优化:使用React Hooks进行state跟踪
作者 | Daishi Kato
译者 | 王文刚
编辑 | Yonie

介绍

React useContext 使用起来非常方便,它可以访问定义 DOM 树中多个组件的全局状态或共享状态。
但是,useContext 不是专为全局状态设计的,并且有一个警告:对上下文值的任何更改都会多播,导致所有 useContext 重新渲染组件。
这篇文章展示了一些关于问题的示例代码以及具有 state 使用跟踪的解决方案。

问题定位

我们以人为对象来举例说明。

const initialState = {
  firstName: 'Harry',
  familyName: 'Potter',
};

我们使用上下文和 local state。

const PersonContext = createContext(null);

const PersonProvider = ({ children }) => {
  const [person, setPerson] = useState(initialState);
  return (
    <PersonContext.Provider value={[person, setPerson]}>
      {children}
    </PersonContext.Provider>
  );
};

最后,这是一个显示人物名字的组件。

const DisplayFirstName = () => {
  const [person] = useContext(PersonContext);
  return (
    <div>First Name: {person.firstName}</div>
  );
};

到现在为止还挺好。
但是,问题是当你更新此人的 family name 的同时,它将触发 DisplayFirstName 重新渲染,甚至渲染结果都是相同的。
请注意,这是不是一个真正的问题,直到它成为一个问题。通常情况下,大多数较小的应用程序都可以正常运行,但是一些较大的应用程序会产生性能问题。

解决方案

让我们看看状态使用跟踪如何解决这个问题。
provider 看起来有点不同,但代码基本相同。

const usePerson = () => useState(initialState);
const { Provider, useTracked } = createContainer(usePerson);

const PersonProvider = ({ children }) => (
  <Provider>
    {children}
  </Provider>
);

DisplayFirstName 组件像这样更改。

const DisplayFirstName = () => {
  const [person] = useTracked();
  return (
    <div>First Name: {person.firstName}</div>
  );
};

注意这个变化?只有区别是 useTracked()而不是 useContext(...)。
通过这个小的更改,跟踪 DisplayFirstName 中的状态使用情况。现在,即使更新了 family name,只要 first name 未更新,该组件就不会重新渲染。
这是轻松的渲染优化。

高级示例

有些读者可能会认为也可以通过类似 useSelector 的 hooks API 来实现。
这是另一个使用 useTracked 的例子。

const initialState = {
  firstName: 'Harry',
  familyName: 'Potter',
  showFullName: false,
};

假设我们有一个像上面这样的 state ,让我们创建一个带有条件的组件。

const DisplayPersonName = () => {
  const [person] = useTracked();
  return (
    <div>
      {person.showFullName ? (
        <span>
          Full Name: {person.firstName}
          <Divider />
          {person.familyName}
        </span>
      ) : (
        <span>First Name: {person.firstName}</span>
      )}
    </div>
  );
};

此组件将在两个场景中重新渲染:
a)当更新 firstName 或 familyName 时,显示全名。
b)当更新 firstName 时,不显示全名。
使用 useSelector 重现相同的行为并不容易,最终可能会在组件中分开。

使用状态来进行跟踪的项目

有两个项目使用状态来跟踪:

  • reactive-react-redux:这是 react-redux 的替代库。它具有相同的 hooks API 和 useTrackedState hook。Github地址:

https://github.com/dai-shi/reactive-react-redux

结束

这篇文章重点介绍了如何轻松使用状态跟踪。篇幅限制,我们并没有讨论这些类库的具体实现。简而言之,我们使用 Proxy API 来跟踪状态使用情况。我们还在 Context API 中使用未记录的功能来停止广播。如果你对这些细节感兴趣,请查看如上所述的 GitHub 库。

上一篇:React劲爆新特性Hooks重构去哪儿网火车票PWA


下一篇:React hooks异步操作踩坑指南