重新选择 - 调用另一个选择器的选择器? [英] Reselect - selector that invokes another selector?

查看:97
本文介绍了重新选择 - 调用另一个选择器的选择器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个选择器:

  const someSelector = createSelector(
getUserIdsSelector,
(ids) => ids.map((id)=> yetAnotherSelector(store,id),
); // ^^^^^(yetAnotherSelector需要2个args)

yetAnotherSelector 是另一个选择器,它接受用户ID - id 并返回一些数据。



然而,因为它是 createSelector ,我无权访问它(我不希望它作为一个函数,因为那时的memoization不起作用)。



是否有以某种方式在 createSelector 中访问商店的方式?还是有其他方法来处理它?<​​/ p>

编辑



我有一个功能:

  const someFunc =(store,id)=> {
const data = userSelector(store,id);
// ^^^^^^^^^^^^ glob al selector
return data.map((user)=> extendUserDataSelector(store,user));
// ^^^^^^^^^^^^^^^^^^^^ selector
}

这样的功能正在杀死我的应用程序,导致所有内容重新渲染并让我疯狂。帮助赞赏。



!!但是:



我做了一些基本的自定义记忆:

 从'lodash'导入{isEqual}; 

const memoizer = {};
const someFunc =(store,id)=> {
const data = userSelector(store,id);
if(memoizer中的id&& isEqual(数据,memoizer(id)){
返回memoizer [id];
}

memoizer [id] = data;
return memoizer [id] .map((user)=> extendUserDataSelector(store,user));
}

它确实这个技巧,但这不仅仅是一种解决方法吗?

解决方案

前言



我遇到了与你相同的情况,遗憾的是没有找到有效的方法从另一个选择器的主体调用选择器。



我说有效的方式,因为你总是可以有一个输入选择器,它传递整个state(store),但这会在每个州的更改中重新计算你的选择器:

  const someSelector = createSelector(
getUserIdsSelector ,
state => state,
(ids,state)=> ids.map((id)=> yetAnotherSelector(state,id)






方法



但是,我找到了两种可能的方法,用于描述的用例下面。我想你的情况类似,所以你可以采取一些见解。



所以情况如下:你有一个选择器,从Store获取特定用户一个id,并且选择器返回特定结构中的User。假设 getUserById 选择器。现在一切都很好,很简单。但是当您希望通过其ID获取多个用户并且还重用以前的选择器时,会出现问题。我们将其命名为 getUsersByIds selector。



1。使用always一个数组,输入id值



第一个可能的解决方案是让一个总是需要一个id数组的选择器( getUsersByIds )和第二个,重用前一个,但它只能获得1个用户( getUserById )。因此,当您只想从商店获得1个用户时,您必须使用 getUserById ,但您必须传递一个只有一个用户ID的数组。



以下是实施:

  import {createSelectorCreator,defaultMemoize来自'重新''
导入{isEqual}来自'lodash'

/ **
*创建一个选择器创建者,它使用`lodash.isEqual`而不是`= ==`
*
*示例用例:当我们将数组传递给选择器时,
*总是重新计算它们,因为默认的`reselect` memoize函数
* treats数组总是作为新实例。
*
* @credits https://github.com/reactjs/reselect#customize-equalitycheck-for-defaultmemoize
* /
const createDeepEqualSelector = createSelectorCreator(
defaultMemoize,
isEqual


export const getUsersIds = createDeepEqualSelector(
(state,{ids})=> ids),ids => ids)

export const getUsersByIds = createSelector(state => state.users,getUsersIds,
(users,userIds)=> {
return userIds.map(id = >({... users [id]})
}


export const getUserById = createSelector(getUsersByIds,users => users [0])

用法:

  //按ID获取1个用户
const user = getUserById(state,{ids:[1]})

//获取尽可能多的用户如你所愿ids
const users = getUsersByIds(state,{ids:[1,2,3]})



2.重用选择器的主体,作为一个独立的功能



这里的想法是将选择器主体的公共部分和可重用部分分开一个独立的功能,所以这个功能可以从所有其他选择器调用。



这是实现:

  export const getUsersByIds = createSelector(state => state.users,getUsers ID,
(用户,userIds)=> {
返回userIds.map(id => _getUserById(users,id))
}


export const getUserById = createSelector(state => state .users,(state,props)=> props.id,_ getUserById)

const _getUserById =(users,id)=> ({...用户[id]})

用法:

  //按ID获取1位用户
const user = getUserById(state,{id:1})

//使用ID获取所需数量的用户
const users = getUsersByIds(state,{ids:[1,2,3]})



结论



方法#1。具有更少的样板(我们没有独立的功能)并且具有干净的实现。



方法#2。更具可重用性。想象一下,当我们调用选择器时,我们没有用户的id,但我们从选择器的主体中获取它作为关系。在这种情况下,我们可以轻松地重用独立功能。这是一个伪示例:

  export const getBook = createSelector(state => state.books,state => state.users ,(州,道具)=> props.id,
(书籍,用户,身份证)=> {
const book = books [id]
//这里我们有作者id(用户的id)
//并且目标是重用`getUserById()`选择器体,
//所以我们的解决方案是重用独立的`_getUserById`函数。
const authorId = book.authorId
const author = _getUserById(users,authorId)

return {
... book,
author
}
}


I have a selector:

const someSelector = createSelector(
   getUserIdsSelector,
   (ids) => ids.map((id) => yetAnotherSelector(store, id),
);                                      //     ^^^^^ (yetAnotherSelector expects 2 args)

That yetAnotherSelector is another selector, that takes user id - id and returns some data.

However, since it's createSelector, I don't have access to store in it (I don't want it as a function because the memoization wouldn't work then).

Is there a way to access store somehow inside createSelector? Or is there any other way to deal with it?

EDIT:

I have a function:

const someFunc = (store, id) => {
    const data = userSelector(store, id);
              // ^^^^^^^^^^^^ global selector
    return data.map((user) => extendUserDataSelector(store, user));
                       //     ^^^^^^^^^^^^^^^^^^^^ selector
}

Such function is killing my app, causing everything to re-render and driving me nuts. Help appreciated.

!! However:

I have done some basic, custom memoization:

import { isEqual } from 'lodash';

const memoizer = {};
const someFunc = (store, id) => {
    const data = userSelector(store, id);
    if (id in memoizer && isEqual(data, memoizer(id)) {
       return memoizer[id];
    }

    memoizer[id] = data;
    return memoizer[id].map((user) => extendUserDataSelector(store, user));
}

And it does the trick, but isn't it just a workaround?

解决方案

Preface

I faced the same case as yours, and unfortunately didn't find an efficient way to call a selector from another selector's body.

I said efficient way, because you can always have an input selector, which passes down the whole state (store), but this will recalculate your selector on each state's changes:

const someSelector = createSelector(
   getUserIdsSelector,
   state => state,
   (ids, state) => ids.map((id) => yetAnotherSelector(state, id)
)


Approaches

However, I found out two possible approaches, for the use-case described below. I guess your case is similar, so you can take some insights.

So the case is as follows: You have a selector, that gets a specific User from the Store by an id, and the selector returns the User in a specific structure. Let's say getUserById selector. For now everything's fine and simple as possible. But the problem occurs when you want to get several Users by their ids and also reuse the previous selector. Let's name it getUsersByIds selector.

1. Using always an Array, for input ids values

The first possible solution is to have a selector that always expects an array of ids (getUsersByIds) and a second one, that reuses the previous, but it will get only 1 User (getUserById). So when you want to get only 1 User from the Store, you have to use getUserById, but you have to pass an array with only one user id.

Here's the implementation:

import { createSelectorCreator, defaultMemoize } from 'reselect'
import { isEqual } from 'lodash'

/**
 * Create a "selector creator" that uses `lodash.isEqual` instead of `===`
 *
 * Example use case: when we pass an array to the selectors,
 * they are always recalculated, because the default `reselect` memoize function
 * treats the arrays always as new instances.
 *
 * @credits https://github.com/reactjs/reselect#customize-equalitycheck-for-defaultmemoize
 */
const createDeepEqualSelector = createSelectorCreator(
  defaultMemoize,
  isEqual
)

export const getUsersIds = createDeepEqualSelector(
  (state, { ids }) => ids), ids => ids)

export const getUsersByIds = createSelector(state => state.users, getUsersIds,
  (users, userIds) => {
    return userIds.map(id => ({ ...users[id] })
  }
)

export const getUserById = createSelector(getUsersByIds, users => users[0])

Usage:

// Get 1 User by id
const user = getUserById(state, { ids: [1] })

// Get as many Users as you want by ids
const users = getUsersByIds(state, { ids: [1, 2, 3] }) 

2. Reuse selector's body, as a stand-alone function

The idea here is to separate the common and reusable part of the selector body in a stand-alone function, so this function to be callable from all other selectors.

Here's the implementation:

export const getUsersByIds = createSelector(state => state.users, getUsersIds,
  (users, userIds) => {
    return userIds.map(id => _getUserById(users, id))
  }
)

export const getUserById = createSelector(state => state.users, (state, props) => props.id, _getUserById)

const _getUserById = (users, id) => ({ ...users[id]})

Usage:

// Get 1 User by id
const user = getUserById(state, { id: 1 })

// Get as many Users as you want by ids
const users = getUsersByIds(state, { ids: [1, 2, 3] }) 

Conclusion

Approach #1. has less boilerplate (we don't have a stand-alone function) and has clean implementation.

Approach #2. is more reusable. Imagine the case, where we don't have an User's id when we call a selector, but we get it from the selector's body as a relation. In that case, we can easily reuse the stand-alone function. Here's а pseudo example:

export const getBook = createSelector(state => state.books, state => state.users, (state, props) => props.id,
(books, users, id) => {
  const book = books[id]
  // Here we have the author id (User's id)
  // and out goal is to reuse `getUserById()` selector body,
  // so our solution is to reuse the stand-alone `_getUserById` function.
  const authorId = book.authorId
  const author = _getUserById(users, authorId)

  return {
    ...book,
    author
  }
}

这篇关于重新选择 - 调用另一个选择器的选择器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆