Redux/Java:管理每个实体的规范化数据和多个模型表示

edd*_*ols 17 javascript reactjs redux

我们正在使用React/Redux构建一个新的应用程序,它是渲染服务器端的.

我们希望遵循Redux的最佳实践,并在服务器进入商店的初始状态之前规范化我们的数据.

对于这个例子,假设我们有一个通用的"Products"实体,它可以非常复杂,并且在商店的根目录和商店根目录的另一个对象中进行规范化.所以结构和Reducers遵循典型的"切片减速器"模式,看起来像这样:

{
  page_x_state: PageReducer
  products: ProductsReducer
}
Run Code Online (Sandbox Code Playgroud)

我们使用联合减速器在将减速器传递到商店之前合并减速器.

理论用例:我们有一个"产品"页面,显示基本产品信息列表.用户可以单击产品以显示模态,然后加载并显示完整的产品数据.

对于上面的示例,从服务器发送的状态将仅包含基本产品模型(3或4个字段),这足以呈现表并且此时获取所有产品信息是浪费且不太高效.

当用户点击产品时,我们将进行AJAX调用以获取该产品的所有数据.获得单个产品的所有数据后,我们是否应该使用完整模型更新产品商店中的实例?如果是这样的话,我们最终会得到一组对象,所有这些对象都可以是不同的状态(有些可能是最小的字段,有些是具有10个字段的完整对象).这是处理它的最佳方式吗?

此外,我有兴趣听到在服务器上管理相同底层模型的不同表示以及如何将其映射到Redux存储(理想情况下为Java)的任何想法.

Mat*_*ent 6

编辑:

明确回答你的第一个问题,如果你的reducer正确构建,你的整个状态树应该初始化,绝对没有数据.但应该是正确的形状.您的Reducer应始终具有默认返回值 - 在呈现服务器端时 - Redux应仅呈现初始状态

在服务器端呈现之后,当存储(现在是客户端)由于用户操作而需要更新时,您的所有产品数据的状态形状已经存在(只是其中一些可能是默认值). .而不是覆盖一个对象,你只需填写空白即可.

比方说,在你的第二个级别的视图需要name,photo_url,pricebrand与初始视图有4种产品就可以了,你的渲染商店将是这个样子:

{
  products: {
    by_id: {
      "1": {
         id: "1",
         name: "Cool Product",
         tags: [],
         brand: "Nike",
         price: 1.99,
         photo_url: "http://url.com",
         category: "",
         product_state: 0,
         is_fetching: 0,
         etc: ""
       },
       "2": {
         id: "2",
         name: "Another Cool Product",
         tags: [],
         brand: "Adidas",
         price: 3.99,
         photo_url: "http://url2.com",
         category: "",
         product_state: 0,
         is_fetching: 0,
         etc: ""
       },
       "3": {
         id: "3",
         name: "Crappy Product",
         tags: [],
         brand: "Badidas",
         price: 0.99,
         photo_url: "http://urlbad.com",
         category: "",
         product_state: 0,
         is_fetching: 0,
         etc: ""
       },
       "4": {
         id: "4",
         name: "Expensive product",
         tags: [],
         brand: "Rolex",
         price: 199.99,
         photo_url: "http://url4.com",
         category: "",
         product_state: 0,
         is_fetching: 0,
         etc: ""
       }
     },
    all_ids: ["1", "2", "3", "4"]
  }
}
Run Code Online (Sandbox Code Playgroud)

您可以在上面的数据中看到一些键只是空字符串或空数组.但是我们拥有页面实际初始渲染所需的数据.

然后,我们可以在服务器呈现并且文档准备就绪后立即在后台对客户端进行异步调用,服务器可能会在用户尝试获取数据之前返回这些初始调用.然后我们可以根据用户请求加载后续产品.我不认为这是最好的方法,但它是对我最有意义的方法.其他一些人可能会有其他想法.这完全取决于您的应用和用例.

我只保留一个产品对象,并保留所有与产品有关的数据.


我最近将一个应用程序部署到生产中,我将分享我的一些见解.该应用程序虽然规模不是太大,但却具有复杂的数据结构,并且作为Redux的新手在制作过程中经历了整个过程(并得到了我的建筑师的指导) - 这些是我们的一些内容.在架构方面没有正确的方法,但肯定有一些事情要避免或做.

在开始写作之前,你的减速器设计一个"静态"状态

如果你不知道你要去哪里,你就无法到达那里.将州的整个结构写成平面将有助于您推断您的州将随着时间的推移而发生变化.我们发现这节省了我们的时间,因为我们不必真正重写大部分.

2.设计你的状态

把事情简单化.Redux的重点是简化状态管理.我们使用了由Dan Abramov创建的关于Redux 的egghead.io教程中的许多技巧.他们很清楚真的有助于解决我们遇到的很多问题.我确定你已经阅读了关于规范化状态的文档,但是他们在我们实现的大多数数据模式中实际执行的简单示例.

而不是创建复杂的数据网络,每个数据块只保留自己的数据,如果它需要引用它的另一部分数据,它只通过id引用它,我们发现这个简单的模式涵盖了我们的大部分需求.

{
  products: {
    by_id: {
     "1": {
        id: "1",
        name: "Cool Product",
        tags: ["tag1", "tag2"],
        product_state: 0,
        is_fetching: 0,
        etc: "etc"
      }
    },
    all_ids: ["1"]
  }
}
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,标签可能是另一块数据,具有使用by_id和的类似数据结构all_ids.在整个文档和啧啧中,Abramov不断引用关系数据和关系数据库,这对我们来说实际上是关键.起初我们一直在关注用户界面,并围绕我们认为我们将如何展示它来设计我们的状态.单击此按钮后,我们开始根据数据与其他数据的关系对数据进行分组,事情开始点击到位.


快速翻转你的问题,我会避免重复任何数据,如另一条评论中提到的,我个人只需在状态对象中创建一个键product_modal.让模态处理它自己的状态......

{
  products: {
    ...
  },
  product_modal: {
    current_product_id: "1",
    is_fetching: true,
    is_open: true
  }
}
Run Code Online (Sandbox Code Playgroud)

我们发现遵循这种模式,页面状态工作得非常好......我们只是将其视为具有id/name等的任何其他数据.


3.减速器逻辑

确保减速器跟踪自己的状态.很多我们的减速器看起来很相似,起初这感觉就像DRY地狱然后我们很快意识到更多减速器的力量...说一个动作被派遣你想要更新一大块状态..没有probs只是检查在你的reducer中为动作而返回新状态.如果您只想更新处于相同状态的一个或两个字段...那么您只需要执行相同的操作,但只能在您想要更改的字段中执行.我们的大多数reducers只是一个switch语句,偶尔会嵌套if语句.

结合减速器

我们没有使用combineReducers,我们编写了自己的.这并不难,它帮助我们了解Redux中发生了什么,它让我们对我们的状态更加聪明.这个啧啧是非常宝贵的

操作

中间件是你的朋友...我们使用带有redux-thunk的 fetch API 来发出RESTful请求.我们将所需的数据请求拆分为单独的操作,这些操作为需要更新调用的每个数据块调用store.dispatch().每个调度都调度了另一个更新状态的操作.这使我们的状态模块化更新,并允许我们更新大型部分,或根据需要进行粒度更新.

处理API

好的,这里有太多的处理方式.我不是说我们的方式是最好的...但它对我们有用.简而言之...我们在java中有一个内部API,具有公开暴露的端点.来自此API的调用并不总是容易映射到前端.我们还没有实现这个,但理想情况下,最初的init端点可能已经写在他们的最后,以获得一些初始数据,这些数据是为了速度而在前端滚动所需的.

我们在与PHP编写的应用程序相同的服务器上创建了一个公共API.此API从前端和浏览器中抽象出内部API的端点(在某些情况下还有数据).

当应用程序向/api/projects/allPHP API 发出GET请求时,将调用我们的内部API,获取必要的数据(有时跨越几个请求)并以redux可以使用的可用格式返回该数据.

这可能不是javascript应用程序的理想方法,但我们没有选择创建新的内部API结构,我们需要使用已存在多年的结构,我们发现性能可以接受.


Jef*_* F. 1

可能没有正确的答案,只有您更喜欢哪种策略:

  1. 最简单的策略是向您的减速器添加另一块名为的组件selectedProduct,并始终使用当前所选产品的完整对象覆盖它。您的模式将始终显示 的详细信息selectedProduct。此策略的缺点是,当用户第二次选择同一产品时,您不会缓存数据,并且您的最小字段未标准化。

  2. 或者,您可以像您所说的那样更新产品商店中的实例,您只需要逻辑来处理它。当您选择一个产品时,如果它已完全加载,则渲染它。如果没有,请进行 ajax 调用,并显示一个微调器,直到其完全加载。