У меня есть следующая структура в моем состоянии 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 я сопоставляю свойство списка полученного раздела и отображаю его данные.При нажатии на строку я создаю компонент 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 не перерисовывается, я все еще вижу старое имя.
Точно такая же методология работает для меня в любом другом случае.Если у меня есть массив или объект в свойстве состояния, и я отображаю его в той же CustomTable - я могу отредактировать его и сразу увидеть результаты (следовательно, у меня есть эти «корневые» и «внутренние» разделения).Но не в этом случае.
Я пытался использовать Object.assign({}, state, {[action.section]:section});
вместо updateObject
метода, но это также не помогло.
Любые идеи, как заставить этот компонент перерисовываться с помощьюизменились свойства?