React - 无需 Redux 的可扩展架构(MVC + DDD 方法)

React - Scalable architecture without Redux (MVC + DDD approach)

简介

React 真的很灵活,似乎我们在编程接口时并没有被强制遵循特定的架构,不像其他库,它就像编码一个普通的视图。对于小型 Web 应用程序,这很酷,但是...一旦您的应用程序开始增长,您编写代码的速度就会逐渐降低,这与您从一开始就定义架构的原则相反。

我的架构

就我而言,我没有使用 Redux 进行状态管理...相反,我使用的是 React Context + React Hooks。

这是我当前的项目结构(使用 firebase 构建的无服务器应用程序):

/app
   /components
      /Activity
      /Authentication
      /Profile
      /Buttons
      /Text
      /Inputs
      /Giphy
      /Messaging
      /HOCs
      ...

   /screens
      /Activity
      /Authentication
      /Profile
      /Messaging
      ...
   
   /contexts
      /Users
      /Content
      /Auth
      ...

   /hooks
      /auth
      /profile
      /users
      /content
      /badges
      /i18n
      ...

   /navigation
      /Stacks
      /Tabs
      ...

   /services
      /third-party
      /firebase 
         /api
      ...

   /lib
   /theme
   /styles
   /utils


/functions (backend)

如您所见,我正在使用某种领域驱动设计来构建我的项目文件。

此外,我正在使用挂钩将关注点与屏幕和组件分开,并在包含相应缩减程序的上下文中管理复杂状态(或需要在路由之间同步的状态)。

在我看来,这像是某种 MVC。视图由我所有的 React 功能组件组成,控制器由我所有的业务和 UI 钩子组成,我的模型的数据包含在上下文中(或者,至少是动态数据,因为高效原因)。

如您所见,我有一个文件夹“services”,它只是我的业务挂钩用来连接到我的服务器(云功能)的接口。

问题

  1. 这个架构有名字吗(flux/redux??)?我的意思是,作为一名 React 程序员,随着时间的流逝,一个又一个错误,我最终以这种“自然”的方式组织了我的项目。

  2. 用钩子拆分我所有的组件逻辑是一种反模式吗?我的意思是,所有 我项目的功能组件只包含事件处理程序或 JSX 来呈现 UI。我已经将每一块代码都移到了钩子上,其中一些包含我的业务逻辑,其他只是与图形界面相关的复杂逻辑(动画,...)

  3. 为了改进我当前的架构,您给我了哪些建议?

  4. 在 React 上下文中使用选择器?我已经实现了一些自定义挂钩,这些挂钩只是从上下文中读取和计算派生数据...因为我不能使用“useSelector”,我不知道这是否是典型的东西,因为它们只是消耗必要的上下文(useContext)和然后执行一些计算。

  5. Redux真的有必要吗?对于一个大中型项目,我已经使用 React Context 很好地处理了它,并且在 hooks 的帮助下我的代码非常干净。你认为随着时间的推移,随着项目的不断发展,有必要迁移到 Redux 吗?

  6. react hooks是应用程序的控制器吗?

嗯,不完全确定这里是否适合提出这样的问题,但从我的角度来看,让我们试着回答这些问题。

答案

  1. 我认为这个特定的架构没有名称(例如,这个架构有一个名称 https://www.freecodecamp.org/news/scaling-your-redux-app-with-ducks-6115955638be/)。在任何情况下,名称都不会是“Flux”或“Redux”,因为这些名称更多地与数据的处理方式相关,而不是项目中文件夹的结构方式。我不认为有一些关于文件夹层次结构的严格规则可以遵循以完全符合 Flux 或 Redux 模式。当然有最佳实践和约定,但它们不是强制性的。
  2. 为了回答这一点,让我分享这篇 link https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0 关于 Dan Abramov 发表的一篇文章。我分享这篇文章是因为最后一次更新(这篇文章的日期是 2015 年,但有一个重要的更新是在 2019 年)。正如您所看到的那样,您似乎做得很好,因为您将核心逻辑放在了钩子中。请注意这一点:您说的是“功能组件”,但我认为您指的是“表示组件”,这是一个重要的区别,因为“功能组件”意味着您的组件基于功能(而不是 class), "presentation component" 表示该组件不包含业务逻辑。 “表示组件”既可以是基于 class 的,也可以是功能性的,功能性组件可以包含业务逻辑(基于 class 的组件正在被功能性组件取代,但这是另一回事了)。
  3. 一些建议:大写和大小写保持一致(你混合了大小写、破折号和驼峰式,通常我喜欢用破折号命名每个文件或文件夹,但这取决于你);确定 HOCs 文件夹是否应该在这里;也许您可以将所有实用程序(libthemestylesutils 本身)放在一个名为 utils 的目录中,其中每个实用程序都命名为 属性;
  4. 关于上下文,这是一个有争议的话题,只是想分享一些从文档 https://reactjs.org/docs/context.html#before-you-use-context and share my opinion on that. The idea behind context is "Context provides a way to pass data through the component tree without having to pass props down manually at every level", as per documentation subtitle. So, basically, it si something created to avoid "property drilling", as exposed here https://medium.com/swlh/avoid-prop-drilling-with-react-context-a00392ee3d8 中获取的注意事项。这只是个人观点,但也许引入 Redux 进行全局状态管理比使用 Context API.
  5. 更好
  6. 不要害怕使用 Redux。如果在使用 Redux 时,你有大量重复的代码行,请害怕。在这种情况下,您应该考虑如何抽象您的操作和缩减器(例如使用操作创建器)。如果您能够概括诸如“从后端获取项目列表”之类的内容,您将意识到您的代码不仅比重复的代码行数更少,而且可读性和连贯性更高。例如,对于列表,您可能有一个像 const getListOfNews = list("NEWS_LIST", "/api/news/"); 这样的操作,其中 list 是一个像 const list = (resource, url) => (params = {}) => dispatch => { // your implementation... }; 这样的操作创建者,类似于 reducer。
  7. 不,它们只是“让您无需编写 class 即可使用状态和其他 React 功能”,如文档中 https://reactjs.org/docs/hooks-intro.html 所述。重要的是要避免尝试使像 MVC 这样的模式适应用不同想法创建的东西,这是一个一般性建议。就像您来自 Angular 并且尝试在 React 中以相同的方式工作。基本上你应该使用 React 或其他 libraries/frameworks,而不是试图将它们从它们的本来面目转变为你已经知道的。