React / Redux为什么特定组件在其兄弟的子组件更新时更新,但其状态不会更改

时间:2018-01-08 18:02:28

标签: reactjs redux react-redux react-router-v4 redux-thunk

更新   旁观者的状态明显不同,但价值不会改变......

enter image description here

详细信息: 有一个布局组件,它接收来自react路由器的路由作为子级。 在布局组件代码中,渲染了两个子组件,Toolbar和sidedrawer,以及包含this.props.children的主要部分。

其中一条路线呈现一个名为page的组件。 Page渲染另一个名为graphContainer的组件,并向其传递一个click事件,该事件将应用于它呈现的graphContainer按钮。

它是如何工作的,我抓住前八个图并显示其中的四个。单击该按钮时,它决定显示下一个4或抓住下一个8。

这整件事都使用了redux。有页面状态,身份验证状态,导航状态和图形状态。单击按钮时唯一的部分状态变化是图形。

但是,GraphContainer和sidedrawer组件一起更新。据我所知,sidedrawer组件中没有任何内容正在发生变化,因此不应触发更新。

在导航状态的redux页面中,交换机命中默认值,只返回状态。 图表redux部分工作得很好,相应地更新。

我的解决方法是在导航减速器状态下实现dontUpdate道具。然后使用shouldComponentUpdate来检查prop,因为默认情况下执行的浅层检查(比如pureComponent)会看到不同的状态或prop。

tl; dr:任何想法为什么sidedrawer组件不断更新,尽管据我所知,没有道具或状态变化?

减速

const graphReducer = (state = initialState, action) => {
...
        case SHOW_NEXTFOUR:
        console.log('SHOW NEXT FOUR', state);
            return {
                ...state,
                ttlShown: action.ttlShown
            };
        default:
            return state;
    }
};

const navReducer = (state = initialState, action) => {
...
        default:
            return {...state, dontUpdate: true};
    }
};

布局组件

class Layout extends Component {
    ...
    handleSideBarOpen = () => {

        this.props.onSidebarToggle();
    }
    render () {
        return (
            <Aux>
                <Toolbar
                    isAuth={this.props.isAuthenticated}
                    drawerToggleClicked={this.handleSideBarOpen}
                />
                <SideDrawer
                    open={this.props.sidebarOpen}
                    closed={this.props.onSidebarToggle} 

                />
                <main className={classes.Content}>
                    {this.props.children}
                </main>
            </Aux>
        )
    }
}

const mapStateToProps = ({ navigation, auth }) => {
    const { sidebarOpen } = navigation;
    const { token } = auth;
    return {
        sidebarOpen,
        isAuthenticated: token !== null 
    };
};

const mapDispatchToProps = {
    onSidebarToggle, getNavTree
};

export default connect(
    mapStateToProps, mapDispatchToProps
)(Layout);

Sidedrawer组件

class sideDrawer extends Component {
    state = {
        popupMenuOpen: false
    }
    shouldComponentUpdate ( nextProps, nextState ) {
        if(nextProps.dontUpdate)
            return false;
        else return true;
    }
…

    render() {
        …
        let navitems = [];

        if(this.props.navData && !this.props.error) {
            navitems = (
                <NavigationItems
                    showClients={this.props.showClientsBtn}
                    navData={this.props.navData}
                    curClientid={this.props.curClientid}
                    curSiteid={this.props.curSiteid}
                    curDashid={this.props.curDashid}
                    curPageid={this.props.curPageid}
                    closeSidebar={this.props.closed}
                    onPageClick={this.handlePageClick}
                    onCSDClick={this.handleOpenPopupMenu}
                />
            );
        } else 
            navitems = <p>Problem Loading Tree</p>;

        return (
            <Aux>
                <div className={attachedClasses.join(' ')}>
                    <div className={classes.Logo}>
                        <div className={classes.CloseWrapper}>
                            <Chip onClick={this.props.closed} className={classes.CloseChip}>X</Chip>
                        </div>

                        <div className={classes.CrumbWrapper}>
                        <Breadcrumbs 
                            backBtn={this.handleBackClick}
                            handleCrumbClick={this.handleCrumbClick}
                            breadcrumbs={this.props.breadcrumbs}
                        />
                        </div>
                    </div>
                    <nav>
                        {navitems}
                        <Popover
                        style={{width: "90%"}}
                          open={this.state.popupMenuOpen}
                          anchorEl={this.state.anchorEl}
                          anchorOrigin={{horizontal: 'middle', vertical: 'bottom'}}
                          targetOrigin={{horizontal: 'middle', vertical: 'top'}}
                          onRequestClose={this.handleClosePopupMenu}
                        >
                          <Menu 
                            style={{width: "87%"}}>
                            {MIs}
                          </Menu>
                        </Popover>
                    </nav>
                </div>
            </Aux>
        );
    }
};

const mapStateToProps = ({ navigation }) => {
    const { dontUpdate, clientid, breadcrumbs,currentPage, selectedClient, selectedSite, selectedDash, selectedPage, navigationData, sidebarOpen, navError } = navigation;

    ...

    }
    return {
        dontUpdate,
        clientid,
        showClientsBtn,
        navData,
        curClientid, 
        curSiteid, 
        curDashid, 
        curPageid,
        parentPageid,
        sidebarOpen,
        navError,
        breadcrumbs,
        currentPage
    };
};

const mapDispatchToProps = {
    getNavTree,
    onPageSelected,
    onSwitchCSD,
    onPageRoute
};

export default withRouter(connect(
    mapStateToProps, mapDispatchToProps
)(sideDrawer));

页面组件

class Page extends Component {
    componentWillMount () {
        this.props.getCurPage();
    }
    render () {
        let content = null;
        if(this.props.location.state && this.props.location.state.currentPage)
            content = (<GraphContainer pageid={this.props.location.state.currentPage} />);

        return this.props.location.state && this.props.location.state.currentPage ? (
            <Aux>
                <p>A PAGE!</p>
                {content}
            </Aux>
        ) : (<Redirect to="/" />);
    }
}

const mapStateToProps = ({pages}) => {
    const { clientid, curPage } = pages;
    return {
        clientid, curPage
    };
};
const mapDispatchToProps = {
    getSelectedPage, getCurPage
};

export default connect(
    mapStateToProps, mapDispatchToProps
)(Page);

图形容器

class GraphsContainer extends Component {
    componentWillReceiveProps(newProps) {
        if(this.props.pageid !== newProps.pageid)
            this.props.getFirstEight(newProps.pageid);
    }
    componentDidMount() {
        if(this.props.pageid)
            this.props.getFirstEight(this.props.pageid);
    }
    handleNextClick = (event) => {
        event.preventDefault();   
        this.props.getNextEight(this.props.pageid, this.props.lastNum, this.props.ttlShown);
    }
    render() {
        let graphcards = null;
        let disableNext = null;

        if (this.props.lastNum >= this.props.ttl)
            disableNext = true;

        if(this.props.graphs && this.props.graphs.length > 0) {
            graphcards = ...
        }
        return (
            <div className={classes.Shell}>
                {graphcards}
                {this.props.lastNum < this.props.ttl ? (
                    <div className={classes.NavBtns}>
                        <RaisedButton disabled={disableNext} onClick={this.handleNextClick}>{'V'}</RaisedButton>    
                    </div>
                ):null}      
            </div>
        );
    }
}

const mapStateToProps = ({pageGraphs}) => {
    const { graphs, ttl, lastNum, ttlShown } = pageGraphs;
    return {
        graphs, ttl, lastNum, ttlShown
    };
};
const mapDispatchToProps = {
    getFirstEight, getNextEight
};

export default connect(
    mapStateToProps, mapDispatchToProps
)(GraphsContainer);

操作

export const getFirstEight = (pageid) => {
    let str = ...;
    return (dispatch) => {
        axios.get( str )
        .then( response => {
            let data = {};
            let graphs;
            let ttl;
            let newLastNum = 0;
            if((typeof response.data !== 'undefined') &&  (response.data !== null)) {
                data = {...response.data};
                ttl = data.total;
                if(ttl <= 8) {
                    graphs = [...data.graphs];
                    newLastNum = ttl;
                } else {
                    graphs = [...data.graphs].slice(0,8);
                    newLastNum = 8;
                }
            }
            dispatch({type: GET_FIRSTEIGHT, payload: {ttl,graphs, lastNum:newLastNum}});
        } )
        .catch( error => {
            console.log('ERROR FETCHING NAV TREE', error);
            dispatch({type: GET_FIRSTEIGHT, payload: {}});
        } );
    };
};
export const getNextEight = (pageid, lastNum, ttlShown) => {
    let str = ...;
    let newLastNum = 0;
    return (dispatch) => {
        if(ttlShown < lastNum) {
            dispatch({type: SHOW_NEXTFOUR, ttlShown: ttlShown+4});
        } else {
            axios.get( str )
            .then( response => {
                // console.log('[RESPONSE]', response);
                let data = {};
                let graphs;
                let ttl;
                if((typeof response.data !== 'undefined') &&  (response.data !== null)) {
                    data = {...response.data};
                    ttl = data.total;
                    if(ttl <= (lastNum+8)) {
                        graphs = [...data.graphs].slice(lastNum);
                        newLastNum = ttl;
                    } else {
                        graphs = [...data.graphs].filter((el,index) => {
                            return (index > (lastNum-1)) && (index < (lastNum+8));
                        });
                        newLastNum = lastNum+8; 
                    }
                }
                dispatch({type: GET_NEXTEIGHT, payload: {ttl,graphs, lastNum:newLastNum, ttlShown: ttlShown+4}});
            } )
            .catch( error => {
                console.log('ERROR FETCHING NAV TREE', error);
                dispatch({type: GET_NEXTEIGHT, payload: {}});
            } );
        }
    };
};

0 个答案:

没有答案