本文将通过逐步实现一个简单的应用来带大家看看如何编写编写高性能的 React 代码。首先会以常规的模式来实现组件,然后再考虑性能的情况下重构每个步骤,并从每个步骤中提取一个通用规则,这些规则可以应用于大多数应用程序。然后比较最后的结果。
下面将编写一个“国家设置”页面,用户可以从列表中选择国家,查看该国家的信息,可以保存国家:
可以看到,左侧有一个国家列表,带有“已保存”和“已选择”状态,当点击列表中的选项时,右侧会显示该国家的详细信息。当按下保存按钮时,选定的国家会变成已保存,已保存的选项的背景会变成蓝色。
1. 构建应用程序
首先,根据设计图,我们要考虑应用的结构以及要实现哪些组件:
- 国家列表组件:将呈现列表中的所有国家,并进行过滤和排序等操作;
- 选定国家组件:将呈现选定国家的详细信息,并具有保存按钮。
当然,这不是实现这个页面的唯一方式,实现方式仅供参考。下面就来看看如何实现这些组件。
2. 实现页面组件
下面终于要开始写代码了,下面就从根开始,实现Page组件,步骤如下:
- 将页面参数中的国家列表数据传递给CountriesList组件,以便它可以呈现这些数据;
- 页面中应该有一个已选中国家的状态,它将从 CountriesList 件接收,并传递给 SelectedCountry 组件;
- 页面中应该有一个已保存国家的状态,它将从 SelectedCountry 组件接收,并传递给 CountriesList 组件。
export const Page = ({ countries }: { countries: Country[] }) => {
const [selectedCountry, setSelectedCountry] = useState(countries[0]);
const [savedCountry, setSavedCountry] = useState(countries[0]);
return (
<>
Country settings</h1>
countries={countries}
onCountryChanged={(c) => setSelectedCountry(c)}
savedCountry={savedCountry}
/>
country={selectedCountry}
onCountrySaved={() => setSavedCountry(selectedCountry)}
/>
</div>
>
);
};
3. 重构页面组件(考虑性能)
在 React 中,当组件的 state 或者 props 发生变化时,组件会重新渲染。在 Page 组件中,当 setSelectedCountry 或者 setSavedCountry 被调用时,组件就会重新渲染。当组件的国家列表数据(props)发生变化时,组件也会重新渲染。CountriesList 和 SelectedCountry 组件也是如此,当它们的 props 发生变化时,都会重新渲染。
我们知道,React 会对 props 进行严格相等的比较,并且内联函数每次都会创建新值。这就导致了一个错误的的观念:为了减少 CountriesList 和SelectedCountry 组件的重新渲染,需要通过在 useCallback 中包装内联函数来避免在每次渲染中重新创建内联函数。
export const Page = ({ countries }: { countries: Country[] }) => {
const onCountryChanged = useCallback((c) => setSelectedCountry(c), []);
const onCountrySaved = useCallback(() => setSavedCountry(selectedCountry), []);
return (
<>
...
onCountryChanged={onCountryChange}
/>
onCountrySaved={onCountrySaved}
/>
...
</>
);
};
而实际上,这样并不会起作用。因为它没有考虑到:如果父组件 Page 被重新渲染,子组件 CountriesList 也总是会重新渲染,即使它根本没有任何 props。
可以这样来简化 Page 组件:
const CountriesList = () => {
console.log("Re-render!!!!!");
return countries list, always re-renders<
/div>;
};
export const Page = ({ countries }: { countries: Country[] }) => {
const [counter, setCounter] = useState(1);
return (
<>
Country settings
h1>