本片文章主要讨论React开发最常犯的10个错误---以及如何解决这些错误。
我们主要介绍一下10个最常见的错误:
- 没有拆分创建组件
- 直接修改state的状态
- 传递props时将数字作为字符串传递
- 不在循环列表时使用key
- 忘记setState是异步方法
- 过度使用 Redux
- 不拆分组件,所有的UI和业务逻辑都在一个组件中
- 不遵循React的文件结构
- 将props作为String发送,而不是Number
- 忘记以大写字母作为组件的名称开头
没有拆分创建组件
在书写React时,最常犯的错误就是没有及时拆分组件,使用React你可创建大型的组件应用于许多任务, 但是最好保持组件的代码量较少,一个组件对应一个功能,这样不仅可以节省我们的时间,也有助于我们调试代码,因为组件足够小,所以更加方便我们调试代码,找到BUG!!
我们来看一个TodoList组件的例子:
// TodoList.jsx
import React from "react";
import { useTodoList } from "../hooks/useTodoList";
import { useQuery } from "../hooks/useQuery";
import TodoItem from "./TodoItem";
import NewTodo from "./NewTodo";
const TodoList = () => {
const { getQuery, setQuery } = useQuery();
const todos = useTodoList();
return (
<div>
<ul>
{todos.map(({ id, title, completed }) => (
<TodoItem key={id} id={id} title={title} completed={completed} />
))}
<NewTodo />
</ul>
<div>
Highlight Query for incomplete items:
<input value={getQuery()} onChange={(e) => setQuery(e.target.value)} />
</div>
</div>
);
};
export default TodoList;
直接修改state的状态
在React中,state是不应该直接被改变的 如果你直接修改了state,会导致难以修复的性能问题,并且页面也不会更新你所修改的值!
我们来看一个不好的例子:
const modifyPetsList = (element, index) => {
petsList[index].checked = element.target.checked;
setPetsList(petsList);
};
这个例子想根据复选框的状态来更新数据对应的checked属性,如果这样就会遇到页面无法更新状态,尽管数据被更改正确,导致这种情况的出现是因为petsList的引用没有发生改变,所以React无法观察和触发重新渲染。
要解决这个问题,可以使用setState()或者useState()钩子函数,这两种方法中的任何一种都将确保我们的更改被 React 确认并且 DOM 被正确重新渲染。
注意: 我们还可以使用map()和spread syntax来避免改变其他状态值。
const modifyPetsList = (element, id) => {
const {checked } = element.target;
setpetsList((pets) => {
return pets.map((pet, index) => {
if (id === index) {
pet = { ...pet, checked };
}
return pet;
});
}) ;
};
传递props时将数字作为字符串传递
在传递 props 时将数字作为字符串传递可能会导致React 程序出现问题。
请看下面的例子:
class Arrival extends React.Component {
render() {
return (
<h1>
Hi! You arrived {this.props.position === 1 ? "first!" : "last!"} .
</h1>
);
}
}
在这个例子中 this.props.position 期望获得一个数字,由于我们使用的是严格比较,所以任何不是数字或不完全等于1的值都会触发 'last' 这个值。
要解决这个问题我们应该这样写:
const element = <Arrival position={1} />;
不在循环列表时使用key
假设我们要在页面上呈现一个列表,代码如下:
const lists = ['cat', 'dog', 'fish’];
render() {
return (
<ul>
{lists.map(listNo =>
<li>{listNo}</li>)}
</ul>
);
}
如果这段代码不涉及元素的正删改查,这可能会奏效。但是在大型列表时,如果想对该列表进行正删改查就会遇到渲染问题。
React 跟踪文档对象模型 (DOM) 上的所有列表元素。如果没有这个记录,React 不会知道你的列表中发生了什么变化。
要解决此问题,您需要为所有列表元素添加key。key给每个元素一个唯一的标识,这有助于 React 确定哪些项目已被添加、删除、修改等。
<ul>
{lists.map(listNo =>
<li key={listNo}>{listNo}</li>)}
</ul>
忘记setState是异步方法
很容易忘记React中的状态是异步的。即使是最有经验的 React 开发人员也会忘记这一点。
异步意味着我们所做的任何修改不会立即生效(并且可能会在下一次渲染时生效)。React 自动批量更新调用以提高性能。如果我们在设置状态值后立即访问它,可能无法获得最准确的结果。
看如下代码:
handlePetsUpdate = (petCount) => {
this.setState({ petCount });
this.props.callback(this.state.petCount); // Old value
};
我们可以通过为 setState() 提供可选的第二个参数来解决此问题,该参数将充当回调函数。使用更改更新状态后,将立即调用回调函数。
handlePetsUpdate = (petCount) => {
this.setState({ petCount }, () => {
this.props.callback(this.state.petCount); // Updated value
});
};
注意:同样的事情也适用于useState(),除了它们没有与 setState() 类似的回调参数。相反,您将使用useEffect()钩子来获得相同的结果。
过度使用 Redux
对于更大的 React 应用程序,许多开发人员使用 Redux 来管理全局状态。
尽管 Redux 很有用,但您无需使用它来管理应用程序中的每个状态。
如果我们的应用没有任何需要交换信息的并行级组件,则无需向项目添加额外的库。当我们使用表单组件并希望在每次访问时检查检查按钮的状态时,建议使用本地状态useState方法或Redux。
不拆分组件,所有的UI和业务逻辑都在一个组件中
这种组件是整体式的,不可重复使用。
它们在 React 中被称为“反模式”。我们不应该构建一个将所有 UI 元素塞进一个组件中的整个页面。相反,我们应该花时间概述应用程序的不同互连部分,并使它们成为自己的组件。当我们以这种方式分离组件时,应用程序的所有部分都更容易在需要时进行维护和重组。
不遵循React的文件结构
我们创建的项目不仅仅用于当前的开发。它们很可能在未来需要维护或操纵。在考虑项目的未来可能性时,文件夹结构非常重要。
我们来看看 ReactJS 社区遵循的标准文件夹结构:
在导航到任何现有项目时,为容器、资产和组件设置单独的位置很有用。遵循命名约定以帮助提高可读性和组织性也很有用。这有助于我们轻松识别项目中编写的任何代码的用途。
将props作为String发送,而不是Number
具有编写大量 HTML 经验的 React 开发人员发现编写如下内容很自然:
<MyComponent value="4" />
这个value props实际上将作为字符串发送到 MyComponent。如果我们确实需要将其作为数字,则可以通过使用类似parseInt()函数或插入大括号而不是引号来解决此问题。
<MyComponent value={4} />
忘记以大写字母作为组件的名称开头
记以大写字母开头组件名称是一个很容易犯的小错误。在 JSX 中,以小写字母开头的组件编译为 HTML 元素。
假如我们写了如下代码:
class demoComponentName extends React.Component {
}
这会导致一个错误,告诉你如果你打算渲染一个 React 组件,你需要以大写字母开头它的名称。
这个错误有一个简单的修复方法,即以大写字母开头的组件名称如下:
class DemoComponentName 扩展 React.Component {
}
往期精彩文章
下篇文章主要想写设计React组件时的5大模式,点个关注不迷路!!