轻松渲染优化:使用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
-
react-tracked:这是一个没有 Redux 依赖的库。本文中的示例基于此。 它具有兼容的 hooks API 和 reactive-react-redux。Github地址:
https://github.com/dai-shi/react-tracked
结束
这篇文章重点介绍了如何轻松使用状态跟踪。篇幅限制,我们并没有讨论这些类库的具体实现。简而言之,我们使用 Proxy API 来跟踪状态使用情况。我们还在 Context API 中使用未记录的功能来停止广播。如果你对这些细节感兴趣,请查看如上所述的 GitHub 库。