在Redux状态下,我具有以下结构:
"equipment" : {
"l656k75ny-r7w4mq" : {
"id" : "l656k75ny-r7w4mq",
"image" : "cam.png",
"isChecked" : false,
"list" : {
"2taa652p5-mongp0" : {
"id" : "2taa652p5-mongp0",
"image" : "dst.png",
"isChecked" : false,
"name" : "Dst"
},
"y3ds1rspk-ftdg2t" : {
"id" : "y3ds1rspk-ftdg2t",
"image" : "flm.png",
"isChecked" : false,
"name" : "Flm"
}
},
"name" : "EQ 1"
},
"lpbixy0f7-bmiwzl" : {
"image" : "prm.png",
"isChecked" : false,
"list" : {
"l40snp2y6-o7gudg" : {
"image" : "tlp.png",
"isChecked" : false,
"name" : "Tlp"
},
"qmxf9bn3v-9x1nky" : {
"image" : "prm.png",
"isChecked" : false,
"name" : "Prm"
}
},
"name" : "EQ 2"
}
}
我有一个Equipment组件(某种高阶组件),在其中我订阅了状态的这一部分,并将其数据传递给CustomExpansionPanel组件:
class Equipment extends Component {
//...different methods
render() {
const { data } = this.props;
let content = error ? <p>Oops, couldn't load equipment list...</p> : <Spinner />;
if ( data.length > 0 ) {
content = (
<Fragment>
<CustomExpansionPanel data={data}/>
</Fragment>
);
}
return (
<SectionContainer>
{content}
</SectionContainer>
);
}
}
const mapStateToProps = state => {
return {
data: state.common.equipment,
};
};
connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(Equipment));
在我的CustomExpansionPanel中,我映射接收到的数据并为每个部分创建一个扩展面板,同时将该部分传递给CustomTable组件,该组件显示为ExpansionPanelDetails内容。
class CustomExpansionPanel extends Component {
//... different methods
render() {
const { data } = this.props;
return (
<Fragment>
{
data.map((section, index) => {
return (
<ExpansionPanel defaultExpanded={false} key={section.id} disabled={!section.hasOwnProperty('list')} >
<ExpansionPanelSummary expandIcon={<StyledIcon classes={{root: classes.expansionIcon}} />}>
<Avatar
alt={section.name}
src={images('./' + section.image)}
classes={{root: classes.avatar}}
/>
<Typography variant='body1' classes={{root: classes.summaryText}}>{section.name}</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<CustomTable data={section} imageVariant={imageVariant} parentId={section.id} />
</ExpansionPanelDetails>
</ExpansionPanel>
);
})
}
</Fragment>
);
}
}
然后在我的CustomTable组件中,映射接收到的节的list属性并显示其数据。单击该行时,我将使用适当的字段构建一个Form组件,并将其显示出来以编辑列表项对象:
class CustomTable extends Component {
//...local state
//...some methods
cellClickHandler = (object, index) => {
this.setState({
editedObject: object,
editedObjectIndex: index
}, () => {
this.setFormData(object);
});
};
handleSaveClicked = data => {
data.id = this.state.editedObject.id;
if(this.props.parentId) {
data.parentId = this.props.parentId;
}
if ( this.state.editedObject && this.state.editedObject.list ) data.list = this.state.editedObject.list;
this.props.onSaveItemEditClicked(this.props.updatedSection, data, this.state.editItemLevel, this.state.editedObjectIndex, () => {
this.handleDialogState();
});
};
componentWillMount() {
if ( this.props.data.list ) {
if ( Array.isArray(this.props.data.list) ) {
this.setState({rows: [...this.props.data]});
} else {
let transformedData = [];
for (let [key, value] of Object.entries(this.props.data.list)) {
let obj = value;
obj['id'] = key;
transformedData.push(obj);
}
this.setState({
rows: [...transformedData],
sortedProperties: sortProperties(Object.keys(transformedData[0]))
});
}
} else if (Array.isArray(this.props.data) && this.props.data.length) {
this.setState({
rows: [...this.props.data],
sortedProperties: sortProperties(Object.keys(this.props.data[0]))
});
}
if (this.props.parentId) {
this.setState({editItemLevel: 'inner'})
}
}
componentWillReceiveProps(nextProps, nextContext) {
if ( nextProps.data.list ) {
if ( Array.isArray(nextProps.data.list) ) {
this.setState({rows: [...nextProps.data]});
} else {
let transformedData = [];
for (let [key, value] of Object.entries(nextProps.data.list)) {
let obj = value;
obj['id'] = key;
transformedData.push(obj);
}
this.setState({
rows: [...transformedData],
sortedProperties: sortProperties(Object.keys(transformedData[0]))
});
}
} else if (Array.isArray(nextProps.data) && nextProps.data.length) {
this.setState({
rows: [...nextProps.data],
sortedProperties: sortProperties(Object.keys(nextProps.data[0]))
});
}
}
render() {
// Table, TableHead, TableRow, TableCell, TableFooter definitions...
<FormDialog
open={this.state.dialogOpen}
title={this.state.dialogTitle}
content={this.state.dialogContentText}
handleClose={this.handleDialogState}
formElements={this.state.formElements}
action={this.handleSaveClicked}
onCancelClicked={this.handleDialogState} />
}
}
const mapStateToProps = state => {
return {
updatedSection: state.control.Update.updatedSection
};
};
const mapDispatchToProps = dispatch => {
return {
onSaveItemEditClicked: (updatedSection, object, level, index, callback) => dispatch(commonActions.onSaveEdit(updatedSection, object, level, index, callback))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(CustomTable));
窗体打开,例如,我可以更改对象的名称,然后单击保存按钮,该按钮将触发onSaveItemEditClicked方法,该方法将调度onSaveEdit操作。
onSaveEdit = (updatedSection, object, level='root', index, callback) => {
return dispatch => {
dispatch(controlActions.onSetSnackbarMessage(SnackbarMessages.PROCESS.UPDATING + ' \'' + capitalize(object.name) + '\''));
dispatch(controlActions.DBProcessStart());
let parentId = null;
let url = '/' + updatedSection + '/' + object.id;
if (level === 'inner') {
url = '/' + updatedSection + '/' + object.parentId + '/list/' + object.id;
parentId = object.parentId;
delete object.parentId;
}
updateData(url, object)
.then(response => {
dispatch(controlActions.onSetSnackbarMessage('\'' + capitalize(object.name) + '\' ' + SnackbarMessages.SUCCESS.UPDATED_SUCCESSFULLY));
dispatch(controlActions.DBProcessSuccess());
if (parentId) {
object.parentId = parentId;
}
dispatch(this[saveEditSuccess](updatedSection, object, level, index));
if (callback != null) {
callback();
}
})
.catch(error => {
console.error('onSaveEdit error', error);
});
}
};
[saveEditSuccess] = (updatedSection, object, level='root', index) => {
return {
type: CommonCommands.UPDATE.SUCCESS,
section: updatedSection,
object: object,
level: level,
index: index
};
};
最后那是我的减速器:
const onUpdateSuccess = (state, action) => {
switch (action.level) {
case 'root':
let section = [...state[action.section]];
section = [
...section.slice(0, action.index),
action.object,
...section.slice(action.index + 1),
];
return updateObject(state, {
[action.section]: section
});
case 'inner':
console.log('inner update success', action);
section = [...state[action.section]];
let parentId = action.object.parentId;
let subsectionIndex = state[action.section].findIndex(member => member.id === parentId);
delete action.object.parentId;
let updatedObject = {...section[subsectionIndex].list[action.object.id], ...action.object};
section[subsectionIndex].list[action.object.id] = updatedObject;
return updateObject(state, {
[action.section]: [...section]
});
default:
return state;
}
};
这是updateObject方法:
const updateObject = (oldObject, updatedProperties) => {
return {
...oldObject,
...updatedProperties
};
};
现在,如果我使用Debug Redux工具检查状态,则可以在对象上看到新名称,并且看到状态确实发生了变化。但是CustomTable组件不会重新呈现,我仍然看到旧名称。
在所有其他情况下,完全相同的方法对我都有效。如果我在state属性中有一个数组或对象,并且将其显示在同一CustomTable中-我可以对其进行编辑并立即查看结果(因此我具有这些“根”和“内部”分隔)。但是在这种情况下不是。
我尝试使用Object.assign({}, state, {[action.section]:section});
而不是updateObject
方法,但这也无济于事。
有什么主意如何使用更改后的属性重新渲染该组件?