状态如何传递给 react-redux 应用程序中的选择器?

How is state passed to selectors in a react-redux app?

我遇到了一个示例,其中 mapStateToProps 函数正在使用记忆。我只是想知道 "state" 参数是如何传递给记忆选择器的。在查看了 reselect 和 redux 的文档之后,似乎 mapStateToProps 可以 return 一个接受状态作为参数的函数,并且连接装饰器可能是将状态传递给它的那个,但我不确定。有人可以解释一下吗?

views/tracklist/index.js

const mapStateToProps = createSelector(
  getBrowserMedia,
  getPlayerIsPlaying,
  getPlayerTrackId,
  getCurrentTracklist,
  getTracksForCurrentTracklist,
  (media, isPlaying, playerTrackId, tracklist, tracks) => ({
    displayLoadingIndicator: tracklist.isPending || tracklist.hasNextPage,
    isMediaLarge: !!media.large,
    isPlaying,
    pause: audio.pause,
    pauseInfiniteScroll: tracklist.isPending || !tracklist.hasNextPage,
    play: audio.play,
    selectedTrackId: playerTrackId,
    tracklistId: tracklist.id,
    tracks
  })
);

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

core/tracklists/selectors.js

export function getCurrentTracklist(state) {
//  console.log(state);
  let tracklists = getTracklists(state);
  return tracklists.get(tracklists.get('currentTracklistId'));
}

export const getTracksForCurrentTracklist = createSelector(
  getCurrentPage,
  getCurrentTrackIds,
  getTracks,
  (currentPage, trackIds, tracks) => {
    return trackIds
      .slice(0, currentPage * TRACKS_PER_PAGE)
      .map(id => tracks.get(id));
  }
);

当我们使用来自 react-redux 的 Connect 组件时状态如何传递给选择器的概述

什么是选择器?

选择器从源中提取数据子集。

让我们将 Redux 商店视为我们的 'front end database'。为了这个目的在数据库中,如果你想提取你执行查询的总数据的一个子集。以类似的方式选择器是我们对 Redux 商店的查询。

在最简单的情况下,选择器可以 return 整个商店的状态。

重新选择文档为我们提供了三个重要的使用选择器的理由

  • 选择器可以计算派生数据,允许 Redux 存储 最小的可能状态。
  • 选择器是高效的。选择器不是 重新计算,除非其参数之一发生变化。
  • 选择器是 可组合的。它们可以用作其他选择器的输入。

什么是高阶分量?

高阶组件是一个采用现有组件并return新组件的函数。

Connect 是一个高阶组件,被赋予了一个选择器

摘自 this 精彩的要点,它很好地解释了连接。

connect() is a function that injects Redux-related props into your component.

Connect 是一个高阶组件,它让我们的 React 组件了解 Redux 存储。当我们调用 connect 时,我们可以传递 mapStateToProps 和 mapDispatchToProps。这些函数定义了 我们的新组件将连接到 redux 存储的方式。

我们可以通过将 mapStateToProps 函数作为参数传递给它访问状态。

我们还可以通过 mapDispatchToProps 将 action creators 绑定到 store.dispatch。这样做的好处是我们不需要传递整个商店来让组件访问 store.dispatch 以便组件可以调度自己的 Redux 操作。

我们传递给Connect的mapStateToProps函数是一个选择器

来自 react-redux 文档

The mapStateToProps function takes a single argument of the entire Redux store’s state and returns an object to be passed as props. It is often called a selector.

将由 mapStateToProps return 编辑的对象视为我们对 Redux 存储的查询结果。结果

The mapStateToProps function should normally return a plain object.

调用 mapStateToProps 的结果通常是一个普通对象,表示我们从 redux 存储中提取的数据。

高阶 Connect 组件允许我们通过将来自这个新对象的数据与组件的现有属性合并来扩展现有组件的功能。

由于选择器只是函数,我们也可以使用连接组件将它们连接到 Redux 存储。

然而在某些情况下我们可以return一个函数。我们为什么要这样做?

通过在 mapStateToProps 中返回一个函数,我们可以劫持组件的渲染周期并优化性能

In advanced scenarios where you need more control over the rendering performance, mapStateToProps() can also return a function. In this case, that function will be used as mapStateToProps() for a particular component instance. This allows you to do per-instance memoization.

通过将 mapStateToProps 函数作为参数传递给我们的高阶组件,只要 Redux 存储中的某些状态发生变化,我们的连接组件就会更新。

如果这些更新发生得非常频繁或者状态树很大,那么 reselect 库就很有用,因为它允许我们使用 memoized 选择器。

这个花哨的词意味着存储选择器调用的结果以备需要再次检索时使用。

因此,如果 mapStatesToProps return 编辑了一个普通对象而不是一个函数,那么每当我们的商店状态发生变化时,我们就会为我们的组件提供新的道具。???

将选择器连接到商店

如果您使用的是 React Redux,则可以在 mapStateToProps() 中将选择器作为常规函数调用:

import { getVisibleTodos } from '../selectors'

const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state)
  }
}

跨多个组件共享选择器

我们可以在使用 reselect 库时像组件一样给 reselect 选择器 props。这使我们能够跨多个组件共享选择器。

假设我们有多个待办事项列表,每个列表都有自己的 ID。我们仍然会为每个待办事项列表实例使用相同的 getVisibleTodos 选择器,但只是将不同的 id 作为 prop 传递。

然而,问题在于 createSelector 仅在其参数集与其先前的参数集相同时才 return 缓存值。

reselect 文档指出我们可以通过 returning mapStateToProps 中的一个函数来克服这个限制:

In order to share a selector across multiple components and retain memoization, each instance of the component needs its own private copy of the selector. If the mapStateToProps argument supplied to connect returns a function instead of an object, it will be used to create an individual mapStateToProps function for each instance of the container.

通过 return 在 mapStateToProps 中调用一个函数,我们可以克服这个限制,记忆将正常工作。

有关更详细的说明,请参阅 this

就是这么简单:举个例子,我有一个这样的mapStateToProps

function mapStateToProps(state) {
  return {
    categoryHistory: getCategoryHistory(state,'extended')
  }
}

然后我创建了一个 选择器,如下所示:

export const getCategoryHistory = (state, type) => createSelector([getTaxonomy, selectedCategoryID], (categories, categoryID) => categories.getIn([type, categoryID]) || [])(state)

解决方案是调用 createSelector() 传递 state 作为参数:

createSelector()(state)

在选择器中你可以使用你想要传递的所有参数。

在您提到的情况下,mapStateToProps 是一个接受状态和 returning 对象的函数。当您将 mapStateToProps 传递给 connect 时,您传递了一个函数,该函数接受 connect 提供的状态作为其参数。

createSelector 创建一个函数,它可以接收状态和 returning 对象。因此,您可以将其分配给 mapStateToProps 并将其传递给 connect。

在文档中,您通常会找到以下内容:

const mapStateToProps = (state) => {
    whatever code
}

export default connect(mapStateToProps, mapDispatchToProps)(Component)

其中 mapStateToProps 接收由 connect 提供的状态参数。

但是,可以让 mapStateToProps 成为一个选择器,如下所示:

const mapStateToProps = createSelector(
    whatever code
)

这是因为 createSelector 可以采用如下状态:

createSelector(whatever code)(state)

和 return 一个对象,就像您在文档中找到的 mapStateToProps 所做的那样。