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

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (44)

我们正在使用React / Redux构建一个新的应用程序,该应用程序将呈现在服务器端。

我们希望遵循Redux的最佳实践,并在服务器传递到商店初始状态之前对服务器上的数据进行规范化处理。

对于这个例子,假设我们有一个通用的'Products'实体,它可能非常复杂,并且根据商店根目录和商店根目录上的另一个对象进行标准化。所以结构和减速器遵循典型的“切片减速器”模式,看起来像这样:

{
  page_x_state: PageReducer
  products: ProductsReducer
}

我们正在使用组合减速器在将减速器传递到商店之前合并减速器。

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

对于上面的例子,从服务器发送的状态将只包含基本的产品模型(3或4个字段),这足以呈现表格并获取所有产品信息,在这一点上是浪费并且性能不高。

当用户点击某个产品时,我们将通过AJAX调用获取该产品的所有数据。一旦我们拥有单一产品的所有数据,我们是否应该使用完整模型更新产品商店中的实例?如果是这样,那么我们最终会得到一组对象,所有这些对象都可能是不同的状态(有些可能具有最小的字段,而有些则是具有10个字段的成熟对象)。这是处理它的最好方法吗?

另外,我希望能够听到有关管理服务器上相同底层模型的不同表示的任何想法,以及如何将其映射到Redux存储(理想情况下是Java)。

提问于
用户回答回答于

我们应该用完整的模型更新产品商店中的实例吗?

应该指出,Java和ReactJs + Redux没有太多概念上的重叠。一切都是一个Javascript对象,而不是一个具有类的对象。

一般来说,将您在Redux商店状态下收到的所有数据存储起来是一种方法。要解决这样一个事实,即一些数据将会最小化,并且一些数据将被完全加载,您应该onComponentWillMount在单个产品展示容器的方法中进行有条件的ajax调用。

class MyGreatProduct extends React.Component {
  onComponentWillMount() {
    if(!this.props.thisProduct.prototype.hasProperty( 'somethingOnlyPresentInFullData' )) {
      doAjaxCall(this.props.thisProduct.id).then((result) => {
        this.props.storeNewResult(result.data);
      }).catch(error=>{ ... })
    }
  }

  // the rest of the component container code
}

const mapStateToProps = (state, ownProps) => {
  return {
    thisProduct: state.products.productInfo[ownProps.selectedId] || {id: ownProps.selectedId}
  }
}

const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    storeNewResult: (data) => { dispatch(productDataActions.fullProductData(data)) }
}

export default connect(mapStateToProps, mapDispatchToProps)(MyGreatProduct);

使用此代码,应该清楚在组件和容器可以关于在任何给定时间在商店中可用的确切数据的不确定性。

就管理服务器上相同底层模型的不同表示以及如何将其映射到Redux存储区而言,我会尝试使用与JSON相同的相对松散度。这应该消除一些耦合。

我的意思是将你拥有的数据添加到JSObject中供React + Redux使用,而不用担心在执行应用程序期间可能存储在Redux状态中的值有多大。

用户回答回答于

明确地回答你的第一个问题,如果你的reducer是正确构建的,那么你的整个状态树应该初始化,而绝对没有数据。但应该是正确的形状。Reducer应该总是有一个默认返回值 - 渲染服务器端时 - Redux应该只呈现初始状态

在服务器端呈现之后,当商店(现在是客户端)需要更新时,由于用户操作,所有产品数据的状态形状已经存在(这只是其中的一部分可能是默认值)。 。可以这么说,而不是重写一个对象,而只是填充空白。

比方说,在你的第二个级别的视图需要namephoto_urlpricebrand与初始视图有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"]
  }
}

你可以在上面的数据中看到一些键只是空字符串或空数组。但是我们有我们需要的数据来实现页面的实际初始渲染。

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

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

我最近部署了一个应用程序,我将分享我的一些见解。该应用程序虽然规模不算太大,但数据结构复杂,作为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"]
  }
}

在上面的例子中,标签可能是另一个具有类似数据结构的数据块,使用by_idall_ids在整个文档和教程中,Abramov始终引用关系数据和关系数据库,这对我们来说确实非常重要。起初,我们一直在关注用户界面,并围绕我们认为我们要展示它的方式来设计我们的状态。当这个点击并且我们开始根据它与其他数据片段的关系来分组数据时,事情就开始点击到位。

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

{
  products: {
    ...
  },
  product_modal: {
    current_product_id: "1",
    is_fetching: true,
    is_open: true
  }
}

我们发现,使用页面状态的这种模式效果非常好,我们只是像处理其他任何带有ID /名称的数据一样对待它。

3.减速器逻辑

确保减速器跟踪他们自己的状态。我们的许多减速器看起来很相似,起初这感觉就像DRY地狱,但后来我们很快意识到更多减速器的力量......说一个动作被派出,并且你想更新一大块状态..没有probs只是检查在你的reducer中进行操作并返回新的状态。如果您只想更新一个或两个相同状态的字段,那么您只需执行相同的操作,但只能在要更改的字段中进行。我们大多数减速器只是简单的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,获取必要的数据(有时会跨越几个请求),并以可用的格式返回数据,这些数据可能会消耗。

这可能不是一个JavaScript应用程序的理想方法,但我们没有选择创建一个新的内部API结构,我们需要使用已经存在好几年的选项,我们发现性能可以接受。

扫码关注云+社区