React 技巧和模式
如果你懂 JavaScript,学习 React 相当容易。然而,在项目规模扩大、重构或重写的过程中,很容易迷失方向,甚至把事情搞砸。我会分享一些技巧,这些技巧真的救了我一命……也节省了我不少时间😇。让我们开始吧!
提示 1:(使用容器)
组件很容易因为代码过多而变得臃肿:API 调用、表单逻辑等等。更糟糕的是,UI 代码也被塞进了这些已经臃肿的组件中。该如何解决这个问题?容器化!容器允许我们将逻辑和 UI 代码隔离到不同的组件中,这有助于我们避免像 MVC 那样导致特定组件臃肿。我们来看一个例子:
该组件获取新闻项目并显示获取的新项目的 UI
const Dashboard = () => {
const [news, newsError] = useCustomFetch("/news");
const [user, userError] = useCustomFetch("/user");
const [trends, trendsError] = useCustomFetch("/trends");
const [notifications] = useCustomFetch("/notifications");
if (news) {
// sort news for tags
// sort news for "sort options"
// perform some custom operations on news
// do something else like caching?
}
if (trends) {
// sort trends for tags
// sort trends for "sort options"
// perform some custom operations on trends
// do something else like caching?
}
if (notifications) {
// sort notifications for tags
// sort notifications for "sort options"
// perform some custom operations on notifications
// do something else like caching?
}
return (
<div>
<h2>user</h2>
loading handler
map cards
display available tags
display sort options
<h2>notifications</h2>
loading handler
map cards
display available tags
display sort options
<h2>Latest News</h2>
loading handler
map cards
display available tags
display sort options
<h2>Trends</h2>
loading handler
map cards
display available tags
display sort options
</div>
);
};
我们在这里跳过了大量的逻辑和 UI 代码,但你几乎可以想象,如果任其自行增长,我们的组件会变得多么庞大。现在,让我们看看这个容器化后的示例。
DashboardContainer
我们可以将整个代码拆分为和,而不是仅仅包含 Dashboard Dashboard
。容器的命名并非强制要求使用 Container ,但这是一个很好的命名约定,就像 MVC 中的控制器一样,例如:UsersController
。
DashboardContainer.jsx
const DashboardContainer = () => {
const [news, newsError] = useCustomFetch("/news");
const [user, userError] = useCustomFetch("/user");
const [trends, trendsError] = useCustomFetch("/trends");
const [notifications] = useCustomFetch("/notifications");
if (news) {
// sort news for tags
// sort news for "sort options"
// perform some custom operations on news
// do something else like caching?
}
if (trends) {
// sort trends for tags
// sort trends for "sort options"
// perform some custom operations on trends
// do something else like caching?
}
if (notifications) {
// sort notifications for tags
// sort notifications for "sort options"
// perform some custom operations on notifications
// do something else like caching?
}
return (
<Dashboard
notifications={notifications}
trends={trends}
news={news}
user={user}
{/* all your other props */}
/>
);
};
现在,您的仪表板组件将如下所示:
const Dashboard = ({ user, notifications, ... }) => {
return (
<div>
<h2>user</h2>
loading handler
map cards
display available tags
display sort options
<h2>notifications</h2>
loading handler
map cards
display available tags
display sort options
<h2>Latest News</h2>
loading handler
map cards
display available tags
display sort options
<h2>Trends</h2>
loading handler
map cards
display available tags
display sort options
</div>
);
};
这样,您可以将所有逻辑放在一个组件中,并通过 props 传递 UI 中所需的所有数据。
提示2:(整洁男人的道具😂)
我给这个技巧起了这么一个滑稽的名字,其实是因为我当时在尝试美化代码、精简代码行数时发现了这一点。这里面具体都包含些什么呢?我们来看一下。在上面这个技巧中,我们像这样传递 props:
<Dashboard
notifications={notifications}
trends={trends}
news={news}
user={user}
/>
这很好,但有时你只需要一些更直接、更容易理解的内容。我们可以用下面的代码替换上面的代码:
const props = { notifications, trends, news, user };
<Dashboard {...props} />
干净、简单、易读😊
提示 3:(错误边界)
根据React 文档,错误边界是指 React 组件,它能够捕获子组件树中任意位置的 JavaScript 错误,记录这些错误,并显示回退 UI 来替代崩溃的组件树。错误边界会在渲染过程中、生命周期方法中以及其下方整个组件树的构造函数中捕获错误。
基本上,应用程序的一部分崩溃不会拖累整个应用程序,而且最重要的是,您可以显示自定义的回退 UI 并记录/报告与应用程序崩溃相关的错误。您只需创建错误边界并将组件作为 props 传递即可。我通常用错误边界包裹整个应用程序。
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
logErrorToMyService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
并包装您想要“保护”的组件
<ErrorBoundary>
<App />
</ErrorBoundary>
就这样。你可以在这里查看文档演示。
提示 4:(选择你的库)
不管你喜不喜欢,库决定了你如何编写和组织代码。你可能有某种方法可以做某事,但库最终会决定它接受什么输入以及如何工作。
我在使用 React 时一直遇到的一个问题是,其他库通常不适合你的 React 应用程序,需要大量的样板代码,或者它们只是有一些奇怪的操作😓 顺便说一下,Redux 满足所有这些标准😓
不过也有一些好消息,只要你仔细寻找,通常总能找到更简单/更精简的选择。例如,大多数项目并不需要 Redux 的所有功能,只需要一个全局状态、或许还需要 Reducer、一个 Setter 和一个 Getter 即可😅 你可以尝试 Zustand、Reactn 和多功能 React Query 等库。
如果您想要更简单的路由体验,您还可以尝试Glass Router,它对整个路由业务采取更友好的方式。
请记住,社区总是有更简单、更小、通常更快的替代方案。
提示 5:(相对导入)
这适用于 CRA 用户
我们通常将资源、视图以及应用中的所有内容放在不同的目录中。这通常会导致使用 导入时出现问题../../..
。有很多解决方案,但最常用的(也是我更喜欢的)方法是重新配置 webpack 以使用相对路径:../../assets
我们可以这样:@/assets
设置
我们基本上想先编辑一下 CRA 设置,而无需eject
事先进行修改。有一些很好的库可以实现这一点,我们将在项目中安装它们:
yarn add react-app-rewired customize-cra
从那里,我们创建一个config-overrides.js
文件并将此代码转储到:
const { override, addWebpackAlias } = require("customize-cra");
const path = require("path");
module.exports = override(
addWebpackAlias({
["@"]: path.resolve(__dirname, "src"),
})
);
从那里,我们转到package.json
脚本部分并替换react-scripts
如下react-app-rewired
内容:
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
}
对于 CRA + JS 用户来说就是这样!
如果您使用带有 CRA 的 TypeScript,则需要添加以下内容,以便编译器不会因为您在导入中使用 @ 而向您大喊大叫。
在您的项目根目录中创建一个新文件tsconfig.base.json
(与您的 package.json 在同一级别)并添加以下内容:
{
"compilerOptions": {
"paths": {
"@/*": [
"src/*"
]
}
},
}
我们不会在主文件中添加它,tsconfig.json
因为 TypeScript 会重写tsconfig.json
并抛出这个错误:
The following changes are being made to your tsconfig.json file:
- compilerOptions.paths must not be set (aliased imports are not supported)
现在要让它工作,您只需要在主tsconfig.json
文件中扩展它:
{
"extends": "./tsconfig.base.json",
您可能需要重启编辑器才能生效(仅限 TypeScript 用户)。之后,您就可以开始替换所有不方便的导入了😇
感谢阅读
这些技巧和窍门帮助我加快了工作流程,保持代码整洁,基本上帮助我克服了懒惰。
如果你有什么想分享的,比如新的技巧、更快的实现我提到的某个功能的方法,或者有什么你不认同的地方,欢迎随时联系我。谢谢!
文章来源:https://dev.to/mychi_darko/react-tips-patterns-4pn3