简体   繁体   English

React-Redux 改变的状态不会重新渲染

[英]React-Redux changed state is not re-rendering

I have built a fairly large react-redux application.我已经构建了一个相当大的 react-redux 应用程序。 In one component I have added an undo feature.在一个组件中,我添加了一个撤消功能。 Tracing the state all the way through it is definitely being updated and not mutated.一直跟踪状态肯定是在更新而不是变异。 It even re-renders the component and all child components.它甚至会重新渲染组件和所有子组件。 However on the page the component position is not modified until I click the component to either move it again or highlight it.然而,在页面上组件位置不会被修改,直到我单击组件再次移动它或突出显示它。

I have definitely verified that this is not a case of mutated state and have stepped through all the redux code to ensure that the shallow equality fails and I have added breakpoints on the main component and the child component which should be moved.我已经确定这不是突变状态的情况,并且已经通过所有 redux 代码来确保浅等式失败,并且我在应该移动的主组件和子组件上添加了断点。

I will add code if you want to see it but my question is why a re-rendered component in React would not re-render in the updated position on the screen, even though the top and left coordinates have definitely changed?如果您想查看它,我会添加代码,但我的问题是为什么 React 中重新渲染的组件不会在屏幕上的更新位置重新渲染,即使顶部和左侧坐标确实发生了变化?

Edit adding code编辑添加代码

//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
    }
}

I have extrapolated all items in the page to separate components as in React redux state change does not cause update even after deep copy of all state data .我已经将页面中的所有项目推断为单独的组件,因为React redux 状态更改即使在所有状态数据的深度复制之后也不会导致更新 This allowed for better stack handling.这允许更好的堆栈处理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM