React是毋庸置疑的最受欢迎的前端JavaScript框架/用户界面库之一。但这并不意味着它是最好的,或者每个人都喜欢它。

React的一些技术原因让人们不喜欢它,令人惊讶的是,其中一个最大的特点也是其中之一——JSX。它是标准JavaScript的扩展,允许您在React组件中使用类似HTML的语法。

React中如此易于识别、明显有助于提高代码可读性和编写代码的简易性的一部分如何变成缺点?这归结于关注点分离。

Separation of concerns

在我们深入讨论之前,我想解释一下“关注点分离”到底是什么,以免遗漏任何微妙之处。

分离关注点意味着在不同的概念/内容之间保持清晰的界限。在编程中,JSX 是忽略这个规则的一个明显例子。现在我们不再把描述组件结构的“模板”和它的逻辑放在不同的 HTML 文件和 JS 文件中,而是将它们(如果你使用 CSS-in-JS,可能还有更多)混合在一起,形成了一些人认为是完美的和谐,而另一些人则认为是无序的混乱。

Personal preference

好的,那么将“视图”和“逻辑”混合在一起会破坏关注点分离。但这真的很糟糕吗?这是不是意味着你总是要将组件的视图和逻辑分开?

不,不是的。首先,缺乏关注点分离并不一定是一件坏事。这是一个开发人员或团队的个人偏好和其他指南的问题。您不必将逻辑和视图分开。但是,如果您这样做,仍然不意味着每个部分都需要一个单独的文件。完美的例子是Vue单文件组件(SFC),或者只是在其中包含<script><style>标签的纯HTML文件。

React hooks

分离关注点是一件事,而React hooks是另一件事。

React Hook 已经存在了相当长的一段时间了(自稳定版本发布以来已经有将近两年的时间),因此它们应该已经被很多人熟知,并且已经被其他许多博客和开发人员完全讲透了。但是让我们再次简要概述一下。

React hooks允许开发者在函数组件中添加状态和使用其他特殊的React功能,而不是以前需要基于类的组件。它们内置了10个钩子(v17.0.1),每个钩子处理不同的React功能,其中只有4个是常用的(useState(), useEffect(),useContext()和useRef()),你也可以自然地创建自己的钩子。而正是这最后一点信息最令我们感兴趣。

Custom hooks

尽管大家很熟悉React hooks,但对于如何创建自己的hooks可能比较陌生。

内置Hooks已经足够构建坚固的React组件,如果还不够的话,在庞大的React生态系统中几乎肯定会有某种开源库,可以将你所需的确切功能“hookify”。那么,如果不必要,为什么还要学习更多关于自定义钩子的知识呢?

Creating a hook

并不是非要去自定义钩子的,但它们肯定可以使你的工作更轻松 - 特别是如果你喜欢关注点的分离。

首先 - 如何创建自定义钩子?自定义钩子只是使用其他钩子的函数。就是这么简单。它还应该遵循“[Hooks规则]()”,如果您正在使用ESLint和适当的[官方配置](),这可以很容易地完成,但这就是它。

老实说,你甚至不必做这些事情 - 使用其他钩子并不是必需的(但相当普遍),如果你的代码质量很好,自定义钩子名称以 use 开头,并且你按照预期使用钩子(在 React 组件的最高层次上),那么你应该没问题。

Examples

这是一个非常简单的挂钩,每秒运行提供的回调函数(因为我想不到更好的方法 🙃):


const useTick = (callback) => {
  const handle = setInterval(() => {
    callback();
  }, 1000);

  return () => {
    clearInterval(handle);
  };
};

这是如何使用他们的方法

const Component = () => {
  const stopTick = useTick(() => {
    console.log("Tick");
  });

  return <button onClick={stopTick}>Stop ticking</button>;
};

如果有一个钩子依赖于另一个钩子,那么这里有一个钩子,通过在后台使用useState()来强制更新您的组件,而不会产生明显的状态变化。

const useForceUpdate = () => {
  const [value, setValue] = useState(true);

  return () => {
    setValue(!value);
  };
};

这是使用它的例子

const Component = () => {
  const forceUpdate = useForceUpdate();

  return <button onClick={forceUpdate}>Update component</button>;
};

附带说明,值得说明的是,通常不应该使用这样的强制更新。大多数情况下,这要么是没有意义的,要么表示您的代码存在潜在错误。唯一的例外是非受控的组件。

Solution proposal

到目前为止,我想你已经看出这个想法的方向了。无论我的例子有多么无意义,它们都有一个共同优点——将逻辑抽象出来,使得主要组件函数看起来更加清晰。

现在,只需将这个想法进行扩展,可能将生成的钩子从组件文件本身移开,然后哇!你在 React 中获得了一个相当不错的关注点分离!

可能看起来像是一个简单的启示,但我只是在不久前才得到它,并且自那时起在我的React项目中使用它,我必须承认-这是一个相当不错的解决方案。

你可能同意我的看法,也可能不同意(请在下方留言),但其实并不重要。我只是提供了一个我认为很不错的代码排列的潜在策略,希望能对你有所帮助。

Best practices

如果你在项目中至少尝试了这样的方法,那么我有一些我个人遵循的“最佳实践”,可能会对你有所帮助。

  • 只有当你的组件逻辑超过10行或有很多较小的hook调用时,才应该使用这种策略;
  • 将你的hook放在一个单独的文件中,这个文件理想情况下不应该有JSX(.js文件而不是.jsx文件);
  • 保持命名一致 - 例如在 logic.js 或 hook.js 中使用钩子(也要使用适当的钩子命名,例如 useComponentNameLogic()),并将组件本身放在单个文件夹的 view.jsx 或 index.jsx 中,可选的 index.js 文件(如果没有被组件所保留)用于重新导出必要的部分。
  • 将只需要简单的回调函数和事件监听器留在JSX文件中,其余的移动到hook中;\
    如果使用处理hook的CSS-in-JS库(例如useStyles()),则将其放置在单独的文件中,如果它不太大,则放置在组件文件的顶部;
  • 记得正确组织你的钩子代码——如果逻辑在不同的组件中被重用,就将一部分分离到外部函数,甚至可能使用更小的钩子。

What do you think?

这是我在React中实现责任分离的建议。它是你必须使用的最佳方法吗?绝对不是,因为根本没有“最佳方法”。再次强调,我只是发现这个方法适合我的需求,我想与你分享,希望它也能帮到你。

原文


晚安啦
36 声望1 粉丝