必须避免的 10 个 React 错误

更新日期: 2023-06-01阅读: 1.2k标签: 错误


这篇文章是作者实际工作经验的总结,以及一些错误使用react的方法,希望能帮助大家改掉这些同样的错误。


1.Props 透传

props传递是将单个道具从父组件向下传递到多个层的做法。理想情况下,道具不应超过两层。当我们选择多层交付时,会造成一些性能问题,这也让React官方很头疼。通过道具可能会导致不必要的重新渲染。因为当 props 改变时,React 组件总是会被重新渲染,而那些不需要 props 而只提供传递函数的中间层组件会被渲染。除了性能问题,props透传会导致数据难以追踪,对于很多想看懂代码的人来说也是一个很大的挑战。

const A = () => {
const [title, setTitle] = useState('')
return <B title={title} />
}
const B = ({ title }) => {
return <C title={title} />
}
const C = ({ title }) => {
return <D title={title} />
}
const D = ({ title }) => {
return <div>{title}</div>
}

有很多方法可以解决这个问题,比如 React Context Hook,或者像 Redux 这样的库。但是使用 Redux 需要一些额外的编码,它更适合单一状态改变很多事情的复杂场景。使用 Context Hook 的简单项目选择是更好的选择。


2.导入比实际使用更多的代码

React 是一个前端框架,它有很多代码量。当我们编写 React 程序时,我们应该避免导入许多未使用的模块。因为它们也会被打包成运行时代码,发送给用户的客户端/浏览器/移动设备。额外的依赖会导致应用体积膨胀,增加用户的加载时间,拖慢网页速度,降低用户体验。

import _ from 'lodash' // entire package import

import _map from 'lodash/map' // only import required packages

为了保证良好的用户体验,我们应该将FCP保持在1.8秒以内,所以我们需要简化代码量。现代打包工具具有 tree-shaking 功能,使用各种方法来缩小和压缩我们用于生产的代码,例如 webpack。但在某些情况下它并不能很好地去除无用代码,最好知道应该打包哪些代码,而不是依赖打包工具来尝试修复我们的代码问题。当前的 JavaScript 经历了多次重大更新,并具有许多新特性。

以前我们需要借助lodash等库来实现这些功能,但是现在lodash的优势正在慢慢减弱。在 youmightnotneed.com/lodash/ 上查看如何用现代 JavaScript 替换 lodash。当然,这取决于您的用户使用的浏览器和 JavaScript 版本。但是我们大多数人使用 babel 或类似的转译器来处理这个问题。现在几乎每个人都在使用 Chrome,对吧?其他图书馆也是如此。


3.不要将业务逻辑与组件逻辑分开

过去,很多人认为 React 组件应该包含逻辑,逻辑是组件的一部分。但放在今天来看,这种观点是有问题的。

const Example = () => {
const [data, setData] = useState([])
useEffect(() => {
fetch('...')
.then(res => res.json())
.then(data => {
const filteredData = data.filter(item => item.status === ture)
setData(filteredData)
})
}, [])
return <div>...</div>
}

将组件和逻辑放在一起使得组件变得复杂,在修改或添加业务逻辑时,对开发人员来说更加复杂,理解整个过程也更具挑战性。

const Example = () => {
const { data, error } = useData()
return <div>...</div>
}

分离组件和逻辑有两个好处:

  1. 专注于分离点。
  2. 重用业务逻辑。


4. 每次渲染重复工作

即使您是经验丰富的 React 老手,您可能仍然对渲染一无所知。渲染很频繁,而且常常出乎意料。这是使用 React 编写组件的核心原则之一,在编写 React 组件时应牢记于心。这也意味着当组件被渲染时,一些逻辑将被重新执行。React 提供了两个 Hook,useMemo 和 useCallback。如果使用得当,这些 Hooks 可以缓存计算结果或函数,减少不必要的重复渲染,最终提升性能。

import React, { useMemo } from 'react'
const MemoExample = ({ items, filter }) => {
const filteredItems = useMemo(() => {
return items.filter(filter )
}, [filter, items])

return filteredItems.map(item => <p>{item}</p>)
}

上面的例子是一个item列表的展示,需要通过一定的条件过滤,最后展示给用户。这种数据过滤在前端是不可避免的,所以我们可以使用useMemo来缓存过滤数据的过程,这样只有当items和filter发生变化时才会重新渲染。


5、useEffect使用不当

useEffect 是 React 中最常用的 Hooks 之一。在class组件时代,componentDidMount是一个通用的生命周期函数,用来做一些数据请求,事件绑定等,在Hooks时代,useEffect已经取代了它。但是不正确地使用 useEffect 最终可能会创建多个事件绑定。以下是错误的用法。

import React, { useMemo } from 'react'
const useEffectBadExample = () => {
useEffect(() => {
const clickHandler = e => console.log('e:', e)
document.getElementById('btn').addEventListener('click', clickHandler)
})

return <button id="btn">click me</button>
}

正确的做法是:

  • useEffect 的回调函数应该返回一个解除绑定的函数。
  • useEffect 应该提供第二个参数,它是一个空数组,并且保证只运行一次。
import React, { useMemo } from 'react'
const UseEffectBadExample = () => {
useEffect(() => {
const clickHandler = e => console.log('e:', e)
document.getElementById('btn').addEventListener('click', clickHandler)
return () => document.getElementById('btn').removeEventListener('click', clickHandler)
}, [])

return <button id="btn">click me</button>
}


6、useState使用不当

useState 也是 React 中使用最多的两个 Hooks 之一。但是让很多人感到困惑的是,useState 可能并没有达到预期的效果。比如一个图片压缩组件:

function Compress() {
const [files, setFiles] = useState([])
const handleChange = (newFiles) => {
api(newFiles).then((res)=>{
const cloneFiles = [...files]// file here is always []
cloneFiles. map(
// some logic...
)
setFiles(cloneFiles)
})
}
return <input type="upload" multiple onChange={handleChange}/>
}

应该改为:

function Compress() {
const [files, setFiles] = useState([])
const handleChange = (newFiles) => {
api(newFiles).then((res)=>{
setFiles((oldFiles) => {
const cloneFiles = [...files]//The file here is the latest
return cloneFiles. map(
// some logic...
)
})
})
}
return <input type="upload" multiple onChange={handleChange}/>
}

原因是该函数基于当前闭包使用的状态。但是状态更新后,会触发渲染并创建新的上下文,而不会影响之前的闭包。因此,要使程序按预期执行,必须使用以下语法

setFiles(oldFiles => [...oldFiles, ...res.data])


7.布尔运算符的错误使用

很多时候我们会使用布尔值来控制页面上某些元素的渲染,这是很正常的事情。还有一些其他方法可以处理此逻辑,最常见的是 && 运算符,这也完全是 JavaScript 的一项功能,但有时会产生意想不到的后果。

const total = 0
const Component = () => total && `Total number of items: ${total}`

当我们需要显示商品数量时,如果数量为0,则只显示0,而不是商品总数:0。原因是JavaScript对待0,所以最好不要依赖JavaScript的boolean true-false比较。正确的做法如下:

const total = 0
const Component = () => {
const hasItem = total > 0
return hasItem && `Total number of items: ${total}`
}


8.处处使用三元表达式进行条件渲染

三元表达式是一种非常简洁的语法,在短代码中非常令人满意。所以很多人喜欢在 React 中使用三元表达式来渲染组件。但它的问题是难以扩展。最简单的三元表达式没有问题,但是多个三元表达式一旦组合在一起,就形成了一个非常大的组件,难以阅读。

import React, { useMemo } from 'react'
const VIPExample = ({ vipLevel }) => {
return (<div>
membership system
{vipLevel === 0 ? (
<button>Activate VIP</button>
) : vipLevel === 1 ? (
<p>Dear Bronze VIP, you have 3 privileges:...</p>
) : vipLevel === 2 ? (
<p>...</p>
) : <p>...</p>}
</div>)
}

这种代码没有功能性错误,但可读性很差。有两种方法可以解决。第一种是使用条件判断代替三元表达式。

import React, { useMemo } from 'react'
const VIPDetail = (vipLevel) => {
if(vipLevel === 0) return <button>Activate VIP</button>
if(vipLevel === 1) return <p>Dear Bronze VIP, you have 3 privileges:...</p>
//...
}
const VIPExample = ({ vipLevel }) => {
return (<div>
membership system
{VIPDetail(vipLevel)}
</div>)
}

如果每个分支中的组件比较复杂,我们更进一步,我们使用抽象来封装组件。

import React, { useMemo } from 'react'
const VIPZeroDetail = ({ vipLevel }) => {
if(vipLevel !== 0) return null
return <button>Activate VIP</button>
}
const VIPOneDetail = ({ vipLevel }) => {
if(vipLevel !== 1) return null
return <p>Dear Bronze VIP, you have 3 privileges:...</p>
}
//...
const VIP = ({ vipLevel }) => {
return <>
<VIPZeroDetail vipLevel={vipLevel} />
<VIPOneDetail vipLevel={vipLevel} />
<!-->...<-->
</>
}
const VIPExample = ({ vipLevel }) => {
return (<div>
membership system
<VIP vipLevel={vipLevel} />
</div>)
}

在大多数情况下,使用条件判断的方法就足够了。使用抽象封装组件的方法有一个缺点,就是组件过于分散,同步逻辑比较麻烦。


9. 不要定义 propTypes 或解构 props

React 中的大部分内容与 JavaScript 中的几乎相同。React props 在 JavaScript 中也只是对象,这意味着我们可以在对象中传递许多不同的值,组件很难知道它们。这使得组件使用 props 更加麻烦。许多人喜欢通过这种方式访问道具。

const Example = (props) => {
return <div>
<h1>{props.title}</h1>
<p>{props.content}</p>
</div>
}

无需使用 TypeScript 或定义 propsTypes,我们可以自由使用 props.xxx 来访问 props。为了解决这个问题,我们可以选择使用 TypeScript 来声明组件 props 的类型。如果您不使用 TypeScript,则可以使用 propTypes。同时,建议以破坏性的方式使用道具。

const Example = ({ title, content }) => {
return <div>
<h1>{title}</h1>
<p>{content}</p>
</div>
}
Example.propTypes = {
title: PropTypes.string.isRequired,
content: PropTypes.string.isRequired
}

这样一来,我们就可以一目了然地看到组件需要哪些 props。当我们尝试访问 props 上不存在的属性时,我们会收到警告。


10.大型应用程序没有代码拆分

大型应用程序意味着大量组件。这时候我们就应该使用代码拆分,将应用拆分成多个js文件,在文件使用的时候加载。这使应用程序的初始包大小保持较小,并允许用户更快地启动网页。react-loadable 是专门处理这个问题的第三方库。使用它,我们可以很好的拆分组件。

import Loadable from 'react-loadable'
import Loading from 'loading'
const LoadableComponent = Loadable({
loader: () => import('./component'),
loading: Loading
})
export default () => <LoadableComponent />


总结

React 为我们提供了强大的开发生态系统和开发工具集,我们可以比以往更轻松地创建 Web 应用程序。然而,它是一套工具,工具可能会被滥用。只有按预期使用工具,并以 JavaScript 优先的方式,才能让我们创建更干净、更强大、性能更高的代码。

作为开发者,不断完善自己的代码,让用户用起来舒服,让其他开发者读起来舒服,是我们应该努力的方向和目标。我的10条建议可以作为你用好React的一个起点,希望能帮助你避免很多在开发过程中容易出现的错误。

翻译来源:https://medium.com/@Choco23/10-react-mistakes-you-must-avoid-6b5102e4c1e1


链接: https://fly63.com/article/detial/12495

解决Cannot read property range of null 错误

vue工程npm run serve/start/dev启动时,node_modules文件报:Cannot read property range of null 错误,该问题是babel-eslint版本更新问题导致的;

HTTP 400 错误 - 请求无效 (Bad request)

在ajax请求后台数据时有时会报 HTTP 400 错误 - 请求无效 (Bad request);出现这个请求无效报错说明请求没有进入到后台服务里;原因:前端提交数据的字段名称或者是字段类型和后台的实体类不一致

js异步错误捕获

我们都知道 try catch 无法捕获 setTimeout 异步任务中的错误,那其中的原因是什么。以及异步代码在 js 中是特别常见的,我们该怎么做才比较?

不能执行已释放Script的代码

父页面初始化声明变量a为数组(数组对象是引用类型,赋值传递的是地址),创建iframe子页面后给父页面变量a赋值,赋值后销毁iframe子页面,再次调用变量a的时候就会抛出异常‘SCRIPT5011:不能执行已释放Script的代码’。

JS错误处理:前端JS/Vue/React/Iframe/跨域/Node

js错误的实质,也是发出一个事件,处理他,error实例对象message:错误提示信息,name:错误名称(非标准属性)宿主环境赋予

nodejs提示 cross-device link not permitted, rename 错误解决方法

文件上传的功能时候,调用fs.renameSync方法错误,这个提示是跨区重命名文件出现的权限问题。先从源文件拷贝到另外分区的目标文件,然后再unlink,就可以了。

Js中使用innerHTML的缺点是什么?

如果在JavaScript中使用innerHTML,缺点是:内容随处可见;不能像“追加到innerHTML”一样使用;innerHTML不提供验证,因此我们可能会在文档中插入有效的和破坏性的HTML并将其中断

Web前端开发,必须规避的8个错误点!

现在,有越来越多所谓的“教程”来帮助我们提高网站的易用性。我们收集了一些在Web开发中容易出错和被忽略的小问题,并且提供了参考的解决方案,以便于帮助Web开发者更好的完善网站。

web前端错误监控

为什么要做前端错误监控?1. 为了保证产品的质量2. 有些问题只存在于线上特定的环境3. 后端错误有监控,前端错误没有监控,前端错误分为两类: 即时运行错误和资源加载错误

自定义错误及扩展错误

当我们在进行开发的时候,通常需要属于我们自己的错误类来反映任务中可能出现的特殊情况。对于网络操作错误,我们需要 HttpError,对于数据库操作错误,我们需要 DbError,对于搜索操作错误,我们需要 NotFoundError,等等

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!