如果你是一名中级React开发人员,希望成为一名高级React开发人员,这篇文章就是为你准备的!
几年来,我每天都在检查初级和中级开发人员编写的React代码,这篇文章涵盖了我所看到的最常见的错误。我假设你已经知道React的基础知识,因此不会涉及“不要改变道具或状态”这样的陷阱。
坏习惯
本节中的每个标题都是你应该避免的坏习惯!
我将使用一个典型的待办事项列表应用程序示例来说明我的一些观点。
重复的 State
每个 state
都应该有一个单一来源。如果同一信息以 state 存储两次,那么这两个state可能会不同步。你可以尝试编写同步两个state 的代码,但这是一个容易出错的地方,而不是解决方案。
这是一个在我们的待办事项列表应用程序上下文中重复状态的例子。我们需要跟踪待办事项列表上的项目,以及哪些项目已经被选中。你可以在状态中存储两个数组,一个数组包含所有的待办事项,另一个数组只包含完成的任务:
代码语言:javascript复制const [todos, setTodos] = useState<Todo[]>([])
const [completedTodos, setCompletedTodos] = useState<Todo[]>([])
但这段代码在最坏的情况下是错误的,在最好的情况下是难闻的!完成的待办事项被存储在状态中两次,所以如果用户编辑待办事项的文本内容,你只调用setTodos, completedTodos现在包含旧的文本,这是不正确的!
有一些方法可以去复制你的状态。在这个虚构的例子中,你可以简单地向Todo类型添加一个完整的布尔值,这样就不再需要completedTodos数组了。
未充分使用 reducers
React有两种内置的方式来存储状态:useState和useReducer。还有无数的库用于管理全局状态,其中Redux是最流行的。由于Redux通过reducers处理所有状态更新,所以我将使用术语“reducer”来同时指代useReducer reducers和Redux reducers。
当状态更新很简单时,useState是非常好的。例如,可以用 usestate
跟踪复选框是否被选中,或者跟踪文本输入的值。
话虽如此,当状态更新变得稍微复杂时,您应该使用一个reducer。特别是,当你在存储一个处于状态的数组时,你应该使用一个reducer。在我们的待办事项列表应用程序的上下文中,你肯定应该使用一个reducer来管理待办事项数组,无论是通过useReducer还是Redux。
Reducers
是有益的,因为:
- 它们提供了一个集中的地方来定义状态转换逻辑。
- 它们非常容易进行单元测试。
- 它们将复杂的逻辑从组件中移出,从而产生更简单的组件。
- 如果同时发生两个更改,它们可以防止状态更新被覆盖。将函数传递给- setState是防止这种情况发生的另一种方法。
- 它们支持性能优化,因为调度具有稳定的标识。
- 他们让你用Immer写突变风格的代码。你可以在useState中使用Immer,但我不认为很多人会这样做。
编写单元测试
开发人员都是很忙的人,编写自动化测试非常耗时。在决定是否应该编写一个测试时,问自己,“这个测试的影响是否足够大,足以证明我花在编写它上的时间是值得的?”如果答案是肯定的,那就写测试吧!
我发现中级React开发人员通常不编写测试,即使测试需要5分钟的时间来编写,并且具有中等或高的影响!我将这些情况称为测试的“低垂果实”。试试低垂的果实!!
在实践中,这意味着为所有包含重要逻辑的“独立”函数编写单元测试。我所说的独立函数是指在React组件之外定义的纯函数。
简化程序就是一个完美的例子!在你的代码库中,任何复杂的reducers都应该有接近100%的测试覆盖率。我强烈推荐使用测试驱动开发开发复杂的简化程序。这意味着您将为减速机处理的每个操作编写至少一个测试,并在编写测试和编写使测试通过的减速机逻辑之间交替进行。
未充分使用 React.memo, useMemo 和 useCallback
在许多情况下,React支持的用户界面可能会变得滞后,特别是当你将频繁的状态更新与渲染成本昂贵的组件(React Select和FontAwesome,我就在看你)配对时。React DevTools是识别渲染性能问题的好工具,可以通过“突出显示组件渲染时的更新”复选框或profiler选项卡。
在对抗糟糕的渲染性能时,你最强大的武器是React.memo
,它只在组件的道具更改时才重新呈现组件。这里的挑战是确保道具不会在每次渲染中改变,在这种情况下React。备忘录不起作用。您需要使用usemmo
和useCallback
钩子来防止这种情况。
我喜欢主动使用React.useMemo和useCallback来防止性能问题发生,但是一种反应性的方法——即等待直到发现性能问题才进行优化——也可以工作。
误用 useEffects
我对React Hooks唯一的不满是useEffect很容易被误用。要成为一名高级React开发人员,你需要完全理解useEffect和依赖数组的行为。
如果你没有使用React Hooks ESLint
插件,你会很容易错过你的效果的一个依赖项,导致一个效果不能像它应该的那样经常运行。这个很容易修复——只需使用ESLint插件并修复警告。
一旦你在依赖项数组中列出了每个依赖项,你可能会发现你的效果运行得太频繁了。例如,该效果可能在每个渲染中运行,并导致无限更新循环。对于这个问题,没有“一刀切”的解决方案,所以您需要分析您的具体情况,以找出问题所在。我要说的是,如果你的效果依赖于一个函数,那么将该函数存储在ref中是一个有用的模式。是这样的:
代码语言:javascript复制const funcRef = useRef(func)
useEffect(() => {
funcRef.current = func
})
useEffect(() => {
// do some stuff and then call
funcRef.current()
}, [/* ... */])
不考虑可用性
作为一名前端开发人员,你应该努力不仅仅是一名程序员。最好的前端开发者也是可用性和网页设计方面的专家,即使这并没有反映在他们的工作头衔上。
可用性只是指应用程序使用起来有多容易。例如,添加一个新的待办事项到列表中有多容易?
如果你有机会与真正的用户进行可用性测试,那就太棒了。我们大多数人都没有这种奢侈,所以我们必须根据直觉来设计界面,了解什么是用户友好的。这在很大程度上可以归结为常识,并观察您每天使用的应用程序中哪些工作,哪些不工作。
以下是一些简单的可用性最佳实践,你今天就可以实现:
- 确保可点击的元素显示为可点击的。将你的光标移动到一个可点击的元素上应该会稍微改变元素的颜色,并使光标变成一个“指向手”,也就是CSS中的指针。将鼠标悬停在一个引导按钮上,看看这些最佳实践的运行情况。
- 不要隐藏重要的UI元素。想象一下,在一个待办事项列表应用程序中,“X”按钮删除待办事项时是不可见的,直到你将鼠标悬停在该待办事项上。有些设计师喜欢这样的“整洁”,但这需要用户四处搜寻,弄清楚如何执行基本操作。
- 用颜色来传达意思。在显示表单时,使用粗体颜色来吸引用户注意提交按钮!如果有一个永久删除某些内容的按钮,它最好是红色的!查看Bootstrap的按钮和提醒来了解这一点。
没有掌握CSS和网页设计
如果你想高效地创建漂亮的ui,你必须掌握CSS和网页设计。我不期望中级开发人员能够立即创建干净和用户友好的界面,同时仍然保持他们的效率高。学习复杂的CSS并建立一种看起来不错的直觉是需要时间的。但你需要朝着这个方向努力,并随着时间的推移变得更好!
要提高你的造型技巧很难给出具体的建议,但这里有一条:掌握flexbox。虽然flexbox一开始可能有些吓人,但它是一个多功能的、功能强大的工具,您可以使用它创建几乎所有日常开发中需要的布局。
这就涵盖了坏习惯!看看你是否犯了这些错误,并努力改进。现在我将缩小并讨论一些可以改善React代码库的最佳实践。
最佳实践
使用 Typescript
普通的JavaScript是一种不错的语言,但是缺少类型检查使得它不适合任何小项目。用TypeScript编写所有的代码将极大地提高应用程序的稳定性和可维护性。
如果你觉得TypeScript太复杂,那就继续做下去。一旦你掌握了流畅的语言,你就能像现在写JavaScript一样快地写TypeScript了。
使用 data-fetching 库
正如我在这篇文章的“坏习惯”部分所说的,正确地编写useEffects是困难的。当您直接使用useEffect从后台的API加载数据时,这一点尤其正确。通过使用一个抽象出数据获取细节的库,您将为自己省去无数的麻烦。我个人更喜欢React Query
,不过RTK Query、SWR
和Apollo
也是很好的选择。
只有在真正需要时才使用服务器渲染
服务器端呈现(SSR)是React最酷的功能之一。它还增加了应用程序的大量复杂性。虽然像Next.js这样的框架使SSR变得更容易,但仍然不可避免地存在必须处理的复杂性。如果您需要使用SSR进行SEO或在移动设备上快速加载,那么请务必使用它。但是,如果您正在编写的业务应用程序没有这些要求,请只使用客户端呈现。你以后会感谢我的。
将样式与组件搭配
应用程序的CSS很快就会变得杂乱无章,没有人能理解。Sass和其他CSS预处理器添加了一些非常棒的功能,但在很大程度上仍然存在与普通CSS相同的问题。
我认为样式应该被定义为单独的React组件,CSS应该和React代码放在一起。将CSS的范围限定在单个组件上,可以将组件重用为共享样式的主要方法,并防止样式意外应用到错误元素上的问题。
在其他类似库的帮助下,你可以通过Emotion、styles-components
或CSS模块来实现组件范围的、并置的样式。我的个人偏好是带有css props
的Emotion
。