如何在Redux中存储关系数据

发布于:2021-02-01 15:10:20

0

310

0

react redux javascript 模式

  

所有前端开发人员迟早都需要将关系数据保存在Redux存储中。

数据

无论它们来自api还是静态有效负载,基于关系数据库与否,数据之间都有关系。

我们将定义资源所有可能与其他资源有关系的数据类型。

示例资源可以是我们个人博客上的一篇文章。

这个帖子将与作者有1:1的关系(从帖子的角度来看,我们将看到一个作者将与帖子有1:N的关系),与评论有1:N的关系。

低效的结构

假设我们的个人博客应该只提供一个帖子列表和每个帖子的详细页面。

对于这个具体的例子,我们可以在Redux存储中有一个一级键,其中包含按id索引的所有文章。在每个文章中,我们可以嵌套作者的数据和评论。

const store = {
  posts: {
    'p123': {
      id: 'p123',
      title: 'Best React pattern in the world!',
      author: {
        id: 'a123',
        name: 'Francesco',
        email: 'name.surname@gmail.com',
      },
      comments: {
        'c123': {
          id: 'c123',
          body: 'Best article ever!',
        },
        'c124': {
          id: 'c124',
          body: 'Best article ever ever!',
        },
      }
    },
  }
}

// And so on, you get the point

如果我想为作者的个人资料创建一个包含他文章列表的页面,就会出现这个问题。

以Redux选择器为例,我们可以这样检索所需的数据:

// This returns an array of posts
const getPostsByAuthor = authorId => state => (
  Object.values(state.posts).filter(post => post.author.id === authorId)
)

// And you'd call this selector like this:
const state = store.getState()
const postsByAuthor = getPostsByAuthor('a123')(state) // [...]

但是,如果能够得到我们需要的东西,效率会特别低:每次我们都应该浏览所有的帖子。

加权结构

加权结构可以是关系数据库中假设表的1:1表示。

const store = {
  posts: {
    'p123': {
      id: 'p123',
      title: 'Best React pattern in the world!',
      author: 'a123',
    },
  },
  author_posts: {
    'a123': ['p123'],
  },
  authors: {
    'a123': {
      id: 'a123',
      name: 'Francesco',
      email: 'name.surname@gmail.com',
    }
  },
  post_comments: {
    'p123': ['c123', 'c124'],
  },
  comments: {
    'c123': {
      id: 'c123',
      body: 'Best article ever!',
      post: 'p123',
    },
    'c124': {
      id: 'c124',
      body: 'Best article ever ever!',
      post: 'p123',
    },
  },
}

在本例中,我们消除了嵌套问题。但是,我们为Redux商店添加了两个新的一级密钥。

这种方法并非完全错误,但随着应用程序的增长,可能很难有效地管理所有关系。

如果资源量有限,这可能是一种有用的方法。但是,如果资源量有限,我们可能并不真正需要Redux,这也是事实。

高效的结构

按照Firebase的建议,我们可以为自己保存一些一级密钥:

const store = {
  posts: {
    data: {
      'p123': {
        id: 'p123',
        title: 'Best React pattern in the world!',
        author: 'a123',
      },
    },
    comments: {
      'p123': ['c123', 'c124'],
    },
  },
  authors: {
    data: {
      'a123': {
        id: 'a123',
        name: 'Francesco',
        email: 'name.surname@gmail.com',
      },
    },
    posts: {
      'a123': ['p123'],
    },
  },
  comments: {
    data: {
      'c123': {
        id: 'c123',
        body: 'Best article ever!',
        post: 'p123',
      },
      'c124': {
        id: 'c124',
        body: 'Best article ever ever!',
        post: 'p123',
      },
    },
  },
}

与Firebase不同,我们不会用“占位符”来嵌套关系。

相反,我们将第一级密钥组织为小的第二级存储容器。

对于reducerscombineReducers函数,您是否有类似的想法?同样的逻辑:我们将我们的全局对象减少到最小的可表示部分。

如何构造选择器

在构建了我们的Redux商店之后,您可能会想到的第一个问题是:如何获取这些数据?

下面是一些简单的选择器。

// Base data

const selectAuthors = state => Object.values(state.authors.data)
const selectAuthor = id => state => state.authors.data[id]

const selectPosts = state => Object.values(state.posts.data)
const selectPost = id => state => state.posts.data[id]

// Totally useless
const selectComments = state => Object.values(state.comments.data)
// Maybe useless
const selectComment = id => state => state.comments.data[id]

// Relations

const selectAuthorPosts = authorId => state => {
  const authorPosts = state.authors.posts[authorId] || []
  return authorPosts.map(postId => selectPost(postId)(state))
}

const selectPostComments = postId => state => {
  const postComments = state.posts.comments[postId] || []
  return postComments.map(commentId => selectComment(commentId)(state))
}

结论

现在您可以构造一个Redux存储来保存关系数据。在某些情况下,它可能有些过分,但在处理更复杂的应用程序时会派上用场。