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 中实现责任拆散的倡议。它是你必须应用的最佳办法吗?相对不是,因为基本没有“最佳办法”。再次强调,我只是发现这个办法适宜我的需要,我想与你分享,心愿它也能帮到你。
原文
- Separation of concerns with custom React hooks — Arek Nawo