我似乎无法弄清楚问题是什么。我有一个名为DisplayItems
的组件,该组件从API(项目列表)中获取一些数据。
我正在使用Redux动作和thunk中间件来从API获取项目和数据。我将项目和其他数据作为对象数组保存到redux存储,并将其命名为entriesData
。实际的数组在entriesData.data
中。我正在使用connect将状态和动作连接到DisplayItems
的道具。
接下来,我有一个名为GalleryItemContentV1
的子组件。 GalleryItemContentV1
未连接到redux。它仅从DisplayItems
接收数据,该数据在项目数组中循环,并将每一项传递给GalleryItemContentV1
。因此,DisplayItems
再遍历this.props.entriesData.data
,并将每个项目作为名为GalleryItemContentV1
的道具传递到entry
。
最后,我在GalleryItemContentV1
中有一个子组件TestCom
。这就是问题所在。我还将从DisplyItem
到TestCom
的项目传递给我。因此,数据流为DisplayItems
(已连接)> GalleryItemContentV1
(未连接)> TestCom
(已连接)。
TestCom
已连接到redux。它使用redux中的一些操作来更新entriesData.data
中的项目。当我在this.props.entriesData.data
中更新TestCom
时,GalleryItemContentV1
可以很好地接收到更新的数据,但是TestCom
却什么也没收到。 TestCom
中的项目永远不会更新。
我发现问题出在将TestCom
连接到redux上。如果TestCom
未连接到redux,它也将收到更新的道具,就像GalleryItemContentV1
一样。
组件如下。我试图尽可能简化它们。
DisplayItems
import React, {Component} from 'react';
import GalleryItemContentV1 from './GalleryItemContentV1';
import { connect } from 'react-redux';
import {getContestEntriesPageData} from '../../actions/contest';
class DisplayItems extends Component {
componentDidMount(){
this.props.getContestEntriesPageData(uri, search);
}
render(){
console.log('DisplayItems props', this.props);
return (
<div>
<div className="card-deck thumbnails" id="card-list">
{ this.props.entriesData.data instanceof Array ?
this.props.entriesData.data.map(function(object, i){
return <GalleryItemContentV1
entry={object} key={i}
arr_key={i}
/>;
}, this) : ''
}
</div>
</div>
)
}
}
const mapStateToProps = state => (
{
entriesData: state.entriesData,
}
)
const mapDispatchToProps = (dispatch) => {
return {
getContestEntriesPageData: (uri, search) => {
dispatch(getContestEntriesPageData(uri, search));
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(DisplayItems);
GalleryItemContentV1
import React, { Component } from 'react';
import TestCom from './TestCom';
class GalleryItemContentV1 extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<TestCom entry={this.props.entry} />
</div>
);
}
}
export default GalleryItemContentV1;
TestCom
我正在this.props.entry.VotesCount
中输出DisplayItems
(来自TestCom
的项目)的结果。这就是问题所在。由于某些原因,当我将TestCom
连接到redux时,它的道具不会更新,但是当它与Redux断开连接时,它的道具就会更新。
import React, {Component} from 'react';
import { connect } from 'react-redux';
class TestCom extends Component {
constructor(props) {
super(props);
}
render(){
console.log('TestCom props', this.props);
return (
<div>{this.props.entry.VotesCount}</div>
)
}
}
const mapStateToProps = state => (
{
user: state.user
}
)
export default connect(mapStateToProps)(TestCom);
这里是我配置redux的地方,但是我不知道有什么要紧的,因为GalleryItemContentV1
看到的更新数据就很好了。
import {createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import ContestReducer from '../reducers/contest';
export default function configureStore(initialState) {
return createStore(
ContestReducer,
initialState,
applyMiddleware(thunk)
);
}
所以,总结一下。
DisplayItems (已连接,可以从商店接收更新的数据)> GalleryItemContentV1 (未连接,可以接收更新)> TestCom (已连接,不接收更新,但是如果断开连接则接收更新)
这是我的减速机,以防万一。
import * as ContestActionTypes from '../actiontypes/contest';
const initialState = {
contest: null,
subscriptions: [],
user: null,
page: null
}
export default function Contest(state=initialState, action) {
console.log('redux action reducer: ', action)
switch(action.type) {
case ContestActionTypes.HANDLE_VOTE:
const newState = { ...state };
if (newState.entriesData) {
const index = newState.entriesData.data.findIndex(x => x.id === action.entry.id)
newState.entriesData.data[index].VotesCount = action.vote;
} else {
newState.entry.VotesCount = action.vote;
}
return newState
default:
return state;
}
}
我还注意到GalleryItemContentV1
也发生了同样的事情。如果我连接了它,那么它将停止从DisplayItems / redux存储接收更新。
任何输入表示赞赏。谢谢!
答案 0 :(得分:3)
您正在改变状态。您正在该Slice Reducer中复制顶层状态的浅层副本,但并未在其中复制所有嵌套层的副本。然后,您连接的组件正在提取state.entriesData
,该引用未更改。因此,connect
包装器组件认为状态根本没有改变,因此不会重新呈现您自己的组件。
Redux docs page on "Immutable Update Patterns"特别指出了这一点,这是一个常见错误。
如果一成不变地更新嵌套状态太麻烦了,我建议您调查immer
immutable update library。另外,我们新的redux-starter-kit
package内部使用了Immer,可让您编写更简单的reducer。
另外,请参阅我的帖子Idiomatic Redux: The History and Implementation of React-Redux,了解有关connect
内部工作方式的背景信息和详细信息。
答案 1 :(得分:0)
是的,我也认为您正在改变自己的状态。以下是reducer的TOGGLE_TODO
案例。我希望此代码段适用于您的代码。
case TOGGLE_TODO:
return Object.assign({}, state, {
todos: state.todos.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: !todo.completed
})
}
return todo
})
})
因此,在您的情况下,如果id匹配,则使用action.vote
进行更新。另请阅读有关深层和浅层对象副本的信息。
答案 2 :(得分:0)
markerikson是正确的,但是我在这里发布了对我有帮助的具体答案。
问题是我试图使用Object.assign({},state,{})的常规方法来更新对象内部的数组(entriesData是对象,entriesData.data是数组)我应该一直在使用JSON.parse(JSON.stringify(state))。我必须阅读Mozilla的解释(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)以了解发生了什么。我之前看过JSON.parse解决方案,但是我并不真正理解浅层和深层克隆,而且,无论浅层克隆是什么,我都不认为我有罪。原来是。
这是我更新的减速器,效果很好。
case ContestActionTypes.HANDLE_VOTE:
const newState = JSON.parse(JSON.stringify(state));
if (newState.entriesData) {
const index = newState.entriesData.data.findIndex(x => x.id === action.entry.id)
newState.entriesData.data[index].VotesCount = action.vote;
} else {
newState.entry.VotesCount = action.vote;
}
return newState;