跨多个组件重新选择仅适用于 deepEqual 检查
reselect across multiple comoponents work only with deepEqual check
我已经以各种方式进行了测试...仍然无法正常工作。
我好像没做错什么
- 与重新选择文档完全相同的代码
- redux store 全部规范化
- reducer 都是不可变的
从 parent 组件,我只是传递一个带有 id 和来自 child 组件的道具,与 redux 连接并使用选择器通过 id(来自 parent组件)
### This is what Parent components render looks like
render() {
return (
<div>
<h4>Parent component</h4>
{this.props.sessionWindow.tabs.map(tabId =>
<ChildComponentHere key={tabId} tabId={tabId} />
)}
</div>
);
}
### This is what Child component looks like
render() {
const { sessionTab } = this.props (this props is from connect() )
<div>
<Tab key={sessionTab.id} tab={sessionTab} />
</div>
))
}
### Selectors for across multiple components
const getTheTab = (state: any, ownProps: IOwnProps) => state.sessionWindows.sessionTab[ownProps.tabId];
const makeTheTabSelector = () =>
createSelector(
[getTheTab],
(tab: object) => tab
)
export const makeMapState = () => {
const theTabSelector = makeTheTabSelector();
const mapStateToProps = (state: any, props: IOwnProps) => {
return {
sessionTab: theTabSelector(state, props)
}
}
return mapStateToProps
}
奇怪的工作解决方案:只需更改为深度相等性检查。(从任何地方)
- 使用具有深度相等性的选择器按预期工作。
- 在 shouldComponentUpdate。使用 _.isEqual 也有效。
.
1. const createDeepEqualSelector = createSelectorCreator(
defaultMemoize,
isEqual
)
2. if (!_isEqual(this.props, nextProps) || !_isEqual(this.state, nextState)){return true}
根据我的理解,我的 redux 始终是不可变的,因此当某些内容发生变化时它会产生新的引用(object 或数组),这就是反应 re-renders 的原因。但是当有 100 个项目并且只有 1 个项目发生变化时,只有更改了 props 的组件才会达到 re-render。
为了做到这一点,我只传递了 id(只是字符串。浅层相等(===)是否有效?)使用这个 id,得到准确的项目。(大多数组件获得相同值的输入但很少组件获取不同值的输入)使用重新选择来记住该值。当某些内容更新并且每个组件都获得新的引用输入时与记忆值进行比较,并且 re-render 当某些内容真正改变时。
这就是我现在能想到的大部分内容...如果我无论如何都必须使用 _isEqual,为什么要使用 reselect?我很确定我在这里遗漏了一些东西。有人可以帮忙吗?
更多说明。(希望..)
首先,我的redux数据结构是这样的
sessionWindow: {
byId: { // window datas byId
"windowId_111": {
id: "windowId_111",
incognito: false,
tabs: [1,7,3,8,45,468,35,124] // this is for the order of sessionTab datas that this window Item has
},
"windowId_222": {
id: "windowId_222",
incognito: true,
tabs: [2, 8, 333, 111]
},{
... keep same data structure as above
}
},
allIds: ["windowId_222", "windowId_111"] // this is for the order of sessionWindow datas
}
sessionTab: { // I put all tab datas here. each sessionTab doesn't know which sessionWindow they are belong to
"1": {
id: 1
title: "google",
url: "www.google.com",
active: false,
...more properties
},
"7": {
id: 7
title: "github",
url: "www.github.com",
active: true
},{
...keep same data structure as above
}
}
问题。
1.当一小部分数据改变时,它re-renders所有其他组件。
假设 id 为 7 url 且标题已更改的 sessionTab。在我的 sessionTab Reducer 中调度了“SessionTabUpdated”操作。这是 reducer 逻辑
const updateSessionTab = (state, action) => {
return {
...state,
[action.tabId]: {
...state[action.tabId],
title: action.payload.title,
url: action.payload.url
}
}
}
什么都没坏。仅使用基本的重新选择并不能阻止其他组件被 re-rendered。我必须使用深度相等版本来停止 re-render 没有数据更改的组件
经过几天的挣扎,我开始认为问题可能出在我的 redux 数据结构上?因为即使我更改了 sessionTab 中的一项,它也总是会像 {...state, [changedTab'id]: {....}} 那样创建新的引用,最后,我不知道...
您的选择器定义和用法的三个方面看起来有点奇怪:
getTheTab
正在同时向下挖掘多个级别
makeTheTabSelector
有一个 "output selector" 只是 returns 它被赋予的值,这意味着它与 getTheTab
相同
- 在
mapState
中,您将整个 props
对象传递给 theTabSelector(state, props)
。
我建议尝试一下,看看会发生什么:
const selectSessionWindows = state => state.sessionWindows;
const selectSessionTabs = createSelector(
[selectSessionWindows],
sessionWindows => sessionWindows.sessionTab
);
const makeTheTabSelector = () => {
const selectTabById = createSelector(
[selectSessionTabs, (state, tabId) => tabId],
(sessionTabs, tabId) => sessionTabs[tabId]
);
return selectTabById;
}
export const makeMapState() => {
const theTabSelector = makeTheTabSelector();
const mapStateToProps = (state: any, props: IOwnProps) => {
return {
sessionTab: theTabSelector(state, props.tabId)
}
}
return mapStateToProps
}
不能保证一定会解决问题,但值得一试。
您可能还想尝试使用一些 devtool 实用程序来告诉您为什么组件是 re-rendering。我在 Devtools#Component Update Monitoring section of my Redux addons catalog.
中有几个这样的工具的链接
希望这能让您弄明白。无论哪种方式,请发表评论让我知道。
我已经以各种方式进行了测试...仍然无法正常工作。 我好像没做错什么
- 与重新选择文档完全相同的代码
- redux store 全部规范化
- reducer 都是不可变的
从 parent 组件,我只是传递一个带有 id 和来自 child 组件的道具,与 redux 连接并使用选择器通过 id(来自 parent组件)
### This is what Parent components render looks like
render() {
return (
<div>
<h4>Parent component</h4>
{this.props.sessionWindow.tabs.map(tabId =>
<ChildComponentHere key={tabId} tabId={tabId} />
)}
</div>
);
}
### This is what Child component looks like
render() {
const { sessionTab } = this.props (this props is from connect() )
<div>
<Tab key={sessionTab.id} tab={sessionTab} />
</div>
))
}
### Selectors for across multiple components
const getTheTab = (state: any, ownProps: IOwnProps) => state.sessionWindows.sessionTab[ownProps.tabId];
const makeTheTabSelector = () =>
createSelector(
[getTheTab],
(tab: object) => tab
)
export const makeMapState = () => {
const theTabSelector = makeTheTabSelector();
const mapStateToProps = (state: any, props: IOwnProps) => {
return {
sessionTab: theTabSelector(state, props)
}
}
return mapStateToProps
}
奇怪的工作解决方案:只需更改为深度相等性检查。(从任何地方)
- 使用具有深度相等性的选择器按预期工作。
- 在 shouldComponentUpdate。使用 _.isEqual 也有效。
.
1. const createDeepEqualSelector = createSelectorCreator(
defaultMemoize,
isEqual
)
2. if (!_isEqual(this.props, nextProps) || !_isEqual(this.state, nextState)){return true}
根据我的理解,我的 redux 始终是不可变的,因此当某些内容发生变化时它会产生新的引用(object 或数组),这就是反应 re-renders 的原因。但是当有 100 个项目并且只有 1 个项目发生变化时,只有更改了 props 的组件才会达到 re-render。
为了做到这一点,我只传递了 id(只是字符串。浅层相等(===)是否有效?)使用这个 id,得到准确的项目。(大多数组件获得相同值的输入但很少组件获取不同值的输入)使用重新选择来记住该值。当某些内容更新并且每个组件都获得新的引用输入时与记忆值进行比较,并且 re-render 当某些内容真正改变时。
这就是我现在能想到的大部分内容...如果我无论如何都必须使用 _isEqual,为什么要使用 reselect?我很确定我在这里遗漏了一些东西。有人可以帮忙吗?
更多说明。(希望..) 首先,我的redux数据结构是这样的
sessionWindow: {
byId: { // window datas byId
"windowId_111": {
id: "windowId_111",
incognito: false,
tabs: [1,7,3,8,45,468,35,124] // this is for the order of sessionTab datas that this window Item has
},
"windowId_222": {
id: "windowId_222",
incognito: true,
tabs: [2, 8, 333, 111]
},{
... keep same data structure as above
}
},
allIds: ["windowId_222", "windowId_111"] // this is for the order of sessionWindow datas
}
sessionTab: { // I put all tab datas here. each sessionTab doesn't know which sessionWindow they are belong to
"1": {
id: 1
title: "google",
url: "www.google.com",
active: false,
...more properties
},
"7": {
id: 7
title: "github",
url: "www.github.com",
active: true
},{
...keep same data structure as above
}
}
问题。 1.当一小部分数据改变时,它re-renders所有其他组件。 假设 id 为 7 url 且标题已更改的 sessionTab。在我的 sessionTab Reducer 中调度了“SessionTabUpdated”操作。这是 reducer 逻辑
const updateSessionTab = (state, action) => {
return {
...state,
[action.tabId]: {
...state[action.tabId],
title: action.payload.title,
url: action.payload.url
}
}
}
什么都没坏。仅使用基本的重新选择并不能阻止其他组件被 re-rendered。我必须使用深度相等版本来停止 re-render 没有数据更改的组件
经过几天的挣扎,我开始认为问题可能出在我的 redux 数据结构上?因为即使我更改了 sessionTab 中的一项,它也总是会像 {...state, [changedTab'id]: {....}} 那样创建新的引用,最后,我不知道...
您的选择器定义和用法的三个方面看起来有点奇怪:
getTheTab
正在同时向下挖掘多个级别makeTheTabSelector
有一个 "output selector" 只是 returns 它被赋予的值,这意味着它与getTheTab
相同
- 在
mapState
中,您将整个props
对象传递给theTabSelector(state, props)
。
我建议尝试一下,看看会发生什么:
const selectSessionWindows = state => state.sessionWindows;
const selectSessionTabs = createSelector(
[selectSessionWindows],
sessionWindows => sessionWindows.sessionTab
);
const makeTheTabSelector = () => {
const selectTabById = createSelector(
[selectSessionTabs, (state, tabId) => tabId],
(sessionTabs, tabId) => sessionTabs[tabId]
);
return selectTabById;
}
export const makeMapState() => {
const theTabSelector = makeTheTabSelector();
const mapStateToProps = (state: any, props: IOwnProps) => {
return {
sessionTab: theTabSelector(state, props.tabId)
}
}
return mapStateToProps
}
不能保证一定会解决问题,但值得一试。
您可能还想尝试使用一些 devtool 实用程序来告诉您为什么组件是 re-rendering。我在 Devtools#Component Update Monitoring section of my Redux addons catalog.
中有几个这样的工具的链接希望这能让您弄明白。无论哪种方式,请发表评论让我知道。