我已经构建了一个相当大的react-redux应用程序。在一个组件中,我添加了撤消功能。一直跟踪状态,绝对是更新状态,而不是突变状态。它甚至重新渲染组件和所有子组件。但是,在页面上,直到我单击该组件以再次移动它或使其突出显示时,该组件的位置才被修改。
我已经确认这不是突变状态的情况,并且逐步执行了所有redux代码以确保浅相等性失败,并且在主要组件和应移动的子组件上添加了断点。 / p>
如果您想查看代码,我将添加代码,但是我的问题是,即使顶部和左侧坐标确实发生了变化,为什么React中重新渲染的组件也不会在屏幕上的更新位置重新渲染? / p>
编辑添加的代码
//layout.js
const mapLayoutDispatchToProps = (dispatch) => {
//add action creators here - by reference?
return {
Layout_Set_Current_Site: (siteId) => { dispatch(Layout_Set_Current_Site(siteId)) },
Layout_Get_Sites: () => { dispatch(Layout_Get_Sites()) },
Layout_Get_Map_Background: (siteId, callback) => { dispatch(Layout_Get_Map_Background(siteId, callback)) },
Layout_Get_UserImages: (deskId) => { dispatch(Layout_Get_UserImages(deskId)) },
Layout_Create_Desk: (type, siteId, height, width) => { dispatch(Layout_Create_Desk(type, siteId, height, width)) },
Layout_Restore_All: () => { dispatch(Layout_Restore_All()) },
Layout_Undo_Change: (callback) => { dispatch(Layout_Undo_Change(callback)) },
Layout_Redo_Change: () => { dispatch(Layout_Redo_Change()) },
Layout_Fetch_Desks: (siteId) => { dispatch(Layout_Fetch_Desks(siteId)) },
Layout_Get_Desk_Types: () => { dispatch(Layout_Get_Desk_Types()) },
Layout_Set_Current_Desk: (desk) => { dispatch(Layout_Set_Current_Desk(desk)) }
};
}
getDesks = () => {
console.log("Layout.getDesks");
// const d = this.props.layout_moveData.desks.present;
const desks = clone(this.props.layout_moveData.present);
return desks;
}
handleKeyPress = (e) => {
console.log("Layout.handleKeyPress");
if (this.state.edit) {
switch (e.code) {
case 'KeyZ':
if (e.ctrlKey) {
this.props.Layout_Undo_Change();
e.cancelBubble = true;
// this.forceUpdate();
}
break;
case 'KeyY':
if (e.ctrlKey) {
//this.props.Layout_Redo_Change();
UndoMove.redo();
e.cancelBubble = true;
}
break;
default:
break;
}
}
}
buildDesks = () => {
const desks = this.getDesks();
let ret = desks.map((desk, index) =>
<Desk
key={index}
desks={desks}
index={index}
deskTypes={this.props.layout.deskTypes}
scale={this.getScale()}
editable={this.state.edit}
/>
);
return ret;
}
render=()=>{
return (
<div>
<Row>
<Col sm={1}>
{this.showAdmin()}
</Col>
<Col sm={10}>
{this.state.details}
</Col>
</Row>
<Row>
<Col sm={1}>
<select onChange={(e) => this.changeMap(e.target)}>
{this.buildMapOptions()}
</select>
</Col>
<Col sm={10} id="Layout_Map_Container">
{this.buildMap()}
{this.buildDesks()}
</Col>
</Row >
{this.showStatus()}
</div>
);
}
}
//desks.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Draggable from '../../Elements/Draggable';
import {
Layout_Clear_Desk,
Layout_Delete_Desk,
Layout_Update_Desk_Data,
Layout_Set_Current_Desk
} from '../../../redux/Creators/Layout_Creator';
import '../../../shared/styles/layout.css';
const clone = require('rfdc')();
const mapDesksStateToProps = (state) => {
return {
layout: state.layout,
layout_moveData: state.layout_moveData
}
}
const mapDesksDispatchToProps = (dispatch) => {
return {
Layout_Clear_Desk: (deskId) => { dispatch(Layout_Clear_Desk(deskId)) },
Layout_Delete_Desk: (deskId) => { dispatch(Layout_Delete_Desk(deskId)) },
Layout_Update_Desk_Data: (desk, deskId) => { dispatch(Layout_Update_Desk_Data(desk, deskId)) },
Layout_Set_Current_Desk: (deskId) => { dispatch(Layout_Set_Current_Desk(deskId)) }
}
}
class Desk extends Component {
constructor(props) {
super(props);
this.state = {
desk: clone(props.desks[props.index]),
desks: clone(props.desks)
}
}
rightClick = (e, deskId) => {
if (this.props.editable) {
const desk = this.state.desk;
let rotation = parseInt(desk.rotation);
rotation += 90;
if (rotation >= 360) rotation -= 360;
desk.rotation = rotation;
this.props.Layout_Set_Current_Desk(desk);
this.props.Layout_Update_Desk_Data(desk);
}
}
updateProperties = (data) => {
let string = `Top: ${data.top}, Left:${data.left}`;
// data = this.state.details + ', ' + data
this.setState({ details: string });
}
mouseUp = (e, deskId, data) => {
console.log("Layout.mouseUp");
const desks = clone(this.state.desks);
// let desk = ;
if (data.dragged && this.props.editable) {
// this.clickDesk(e, deskId);
const scale = this.props.scale;
const newX = parseInt(data.left / scale);
const newY = parseInt(data.top / scale);
desks[deskId].x = newX + ""; //convert to strings
desks[deskId].y = newY + "";
console.log(this.state.desks);
console.log(desks);
this.props.Layout_Update_Desk_Data(desks, deskId);
}
else {
this.clickDesk(e, deskId);
}
}
clickDesk = (e, deskId) => {
if (deskId !== null && deskId !== undefined && deskId !== false) {
this.props.Layout_Set_Current_Desk(this.state.desk);
}
else {
this.props.Layout_Set_Current_Desk(null);
}
}
render() {
let deskImg = null;
// const desk = this.props.desks[this.props.index];
let desk = clone(this.state.desk);
try {
let dImg = this.props.deskTypes.find(
d => parseInt(d.deskType) === parseInt(desk.deskType)
);
deskImg = dImg.deskImage;
}
catch (ex) {
console.log(ex);
}
const userName = desk.UserLogon !== (null || '') ? desk.UserLogon : "Unassigned";
const top = Math.trunc(parseInt(parseInt(desk.y) * this.props.scale));
const left = Math.trunc(parseInt(parseInt(desk.x) * this.props.scale));
let imgStyle = {
width: `${parseInt(parseInt(desk.width) * this.props.scale)}px`,
height: `${parseInt((parseInt(desk.height) * this.props.scale))}px`,
position: 'absolute'
}
if (this.props.layout.currentDesk && desk.id === this.props.layout.currentDesk.id) {
imgStyle.border = '2px solid cyan';
}
const url = `data:image/jpeg;base64,${deskImg}`;
try {
//
return (
<Draggable key={desk.id}
index={this.props.index}
enabled={this.props.editable}
left={left}
top={top}
onMove={this.updateProperties}
onStop={this.mouseUp}
onRightClick={this.rightClick}
>
<div style={{
position: 'relative',
transform: `rotate(${parseInt(desk.rotation)}deg)`
}}
className='deskImg'>
<img style={imgStyle} alt={userName} src={url} />
</div>
</Draggable>
);
}
catch (ex) {
console.log(ex);
return null;
}
}//buildDesks
}
export default connect(mapDesksStateToProps, mapDesksDispatchToProps)(Desk);
//layout_creators.js
export const Layout_Undo_Change = () => (dispatch, getState) => {
const state = getState();
const desks = clone(state.layout_moveData);
console.log("1", state.layout_moveData.present)
//if no past to undo to
if (desks.past.length === 0) return;
const previous = clone(desks.past[desks.past.length - 1]);
desks.past.shift();
const undoPast = clone(desks.past);
// const undoPast = clone(desks.past).slice(0, desks.past.length - 1);
const undoFuture = [clone(desks.present), ...clone(desks.future)]
const undoDesks = { past: undoPast, present: previous, future: undoFuture };
dispatch({ type: ActionTypes.LAYOUT_UNDO_MOVES, payload: clone(undoDesks) });
console.log("2", state.layout_moveData.present)
let init = fetchInit();
init.method = "POST";
const deskData = { mode: 'UPDATEMANY', data: previous };
init.body = JSON.stringify(deskData);
let myReq = new Request(`/dataAPI/Layout/`, init);
fetch(myReq)
.then((response) => {
if (response.ok) {
return response;
}
else {
var error = new Error("Error " + response.statusText);
error.response = response;
throw error;
}
}, (error) => {
var err = new Error(error.message);
throw err;
})
.catch(err => {
return dispatch({
type: ActionTypes.LAYOUT_FAILED,
payload: err.message
});
});
}
//layout_reducer.js
import * as ActionTypes from '../ActionTypes';
export const layout = (state = {
isLoading: true,
isLoadingMap: false,
isLoadingDesks: false,
desksLoaded: false,
mapLoaded: false,
currentMap: null,
currentDesk: null,
maps: [],
deskTypes: [],
editMode: false,
errMess: null
}, action) => {
switch (action.type) {
case ActionTypes.LAYOUT_SITES_LOADING:
return { ...state, isLoading: true };
case ActionTypes.LAYOUT_DESKS_LOADING:
return { ...state, isLoadingDesks: true, desksLoaded: false };
case ActionTypes.LAYOUT_MAP_LOADING:
return {
...state, isLoadingMap: true, desks: [],
currentDesk: null, editMode: false, desksLoaded: false
};
case ActionTypes.LAYOUT_MAP_LOADED:
return { ...state, isLoadingMap: false, mapLoaded: true, maps: action.payload };
case ActionTypes.LAYOUT_MAPS_LOADED:
return { ...state, maps: action.payload, isLoading: false };
case ActionTypes.LAYOUT_DESKTYPES_LOADED:
return { ...state, deskTypes: action.payload };
case ActionTypes.LAYOUT_SET_CURRENT_DESK:
return { ...state, currentDesk: action.payload };
case ActionTypes.LAYOUT_SET_EDITMODE:
return { ...state, editMode: action.payload };
case ActionTypes.LAYOUT_DESK_DELETED:
return { ...state, currentDesk: null }
case ActionTypes.LAYOUT_DESKS_LOADED:
return { ...state, currentDesk: null, isLoadingDesks: false, desksLoaded: true }
case ActionTypes.LAYOUT_SET_ACTIVE_MAP:
return { ...state, currentMap: action.payload, desksLoaded: false };
case ActionTypes.LAYOUT_FAILED:
return {
...state, isLoadingMap: false, isLoadingDesks: false, desksLoaded: false,
errMess: action.payload, pageUsageData: []
};
case ActionTypes.LAYOUT_RESTORE_ALL:
return {
...state,
isLoading: true, isLoadingMap: false, mapLoaded: false, currentMap: null,
maps: [], desks: [], deskTypes: [], editMode: false,
errMess: null, desksLoaded: false
}
default:
return state;
}
}
export const layout_moveData = (state = {
past: [],
present: null,
future: []
}, action) => {
switch (action.type) {
case ActionTypes.LAYOUT_DESKS_LOADING:
return { ...state, present: [], past: [], future: [] };
case ActionTypes.LAYOUT_DESKS_LOADED:
return { ...state, present: action.payload, past: [], future: [] };
case ActionTypes.LAYOUT_DESK_DELETED:
return { ...state, present: action.payload.present, past: action.payload.past, future: action.payload.future };
case ActionTypes.LAYOUT_RESTORE_ALL:
return { ...state, present: [], past: [], future: [] };
case ActionTypes.LAYOUT_SET_MOVES:
return { ...state, present: action.payload.present, past: action.payload.past, future: action.payload.future };
case ActionTypes.LAYOUT_UNDO_MOVES:
return { ...state, present: action.payload.present, past: action.payload.past, future: action.payload.future };
case ActionTypes.LAYOUT_REDO_MOVES:
return { ...state, present: action.payload.present, past: action.payload.past, future: action.payload.future };
default:
return state
}
}