【问题标题】:ComponentWillMount keeps being called repeatelyComponentWillMount 不断被重复调用
【发布时间】:2016-10-24 05:11:32
【问题描述】:

我正在尝试调用后端 api 以在页面加载时获取用户的个人资料:

鉴于以下操作:

export const GET_MY_PROFILE_START = 'GET_MY_PROFILE_START';
export const GET_MY_PROFILE_ERROR = 'GET_MY_PROFILE_ERROR';
export const GET_MY_PROFILE_SUCCESS = 'GET_MY_PROFILE_SUCCESS';

let a = 0;
export function getMyProfile() {
    a = a+1;
    window.console.log("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
    window.console.log(a);
    return dispatch => {
    window.console.log("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
        dispatch(getMyProfileStart());
    $.ajax({
            type: 'GET',
            url: getMyProfileURL,
            contentType: "application/json",
            dataType: 'json',
        }).done(function(res){
            if (!res.values || res.values.count > 0) {
                dispatch(getMyProfileSuccess(res.data))
            } else {
                dispatch(getMyProfileError())
            }
        }).fail(function(error) {
                dispatch(getMyProfileError())
        })
    }
}

function getMyProfileStart() {
    return {
        type: GET_MY_PROFILE_START,
    }
}

function getMyProfileSuccess(profile) {
    return {
        type: GET_MY_PROFILE_SUCCESS,
        profile: profile,
    }
}

function getMyProfileError() {
    return {
        type: GET_MY_PROFILE_ERROR,
    }
}

以及以下减速器:

import { SET_USER_PROFILE, CLEAR_USER_PROFILE, GET_MY_PROFILE_START, GET_MY_PROFILE_ERROR, GET_MY_PROFILE_SUCCESS } from '../serActions'

export default (state = {
    loggedIn: false,
    profiledLoading: false,
    profile: {},
}, action) => {
    switch (action.type) {
        case SET_USER_PROFILE:
            return {
                loggedIn: true,
                profiledLoading: false,
                profile: {},
            }
        case CLEAR_USER_PROFILE:
            return {
                loggedIn: false,
                profiledLoading: false,
                profile: {},
            }
        case GET_MY_PROFILE_START:
            return {
                loggedIn: false,
                profiledLoading: true,
                profile: {},
            }
        case GET_MY_PROFILE_ERROR:
            return {
                loggedIn: true,
                profiledLoaded: false,
                profile: {},
            }
        case GET_MY_PROFILE_SUCCESS:
            return {
                loggedIn: true,
                profiledLoading: false,
                profile: action.profile,
            }
        default:
            return state
    }
}

以及以下组件:

class AvatarButton extends Component {
    componentWillMount() {
        window.console.log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        this.props.dispatch(getMyProfile())
    }

    render() {
        //const { profile, toggleMenu, showHideMenu } = this.props
        const { user } = this.props
        window.console.log(user);
        return (
            <a className="user-button nav-button avatar-button"
                onClick={user.toggleMenu}
                onBlur={this.hideMenu.bind(this)}
            >
                <i className="glyphicon glyphicon-user"/>
            </a>
        )
    }

    hideMenu() {
        this.props.user.showHideMenu(false)
    }
}

AvatarButton.propTypes = {
    toggleMenu: PropTypes.func.isRequired,
    showHideMenu: PropTypes.func.isRequired,
}

function select(state) {
    return {
        user: state.user,
    }
}

// Wrap the component to inject dispatch and state into it
export default connect(select)(AvatarButton)

该组件用于:

class UserNavContainer extends Component {
    constructor(props) {
        super(props)
        this.state = {
            displayMenu: false,
        }
    }
    render() {
        const { dispatch, user, pathname } = this.props
        if (!user.loggedIn)
            return (
                <LoginButton pathname={pathname}/>
            )

        return (
            <div className="user-nav">
                <AvatarButton
                    toggleMenu={this.toggleMenu.bind(this)}
                    showHideMenu={this.showHideMenu.bind(this)}
                />
                <UserMenu
                    visible={this.state.displayMenu}
                    logoutHandler={this.logout.bind(this)}
                    hideMenu={this.showHideMenu.bind(this, false)}
                />
            </div>
        )
    }

    logout() {
        window.location = "some url"
    }

    toggleMenu() {
        this.showHideMenu(!this.state.displayMenu)
    }

    showHideMenu(show) {
        this.setState({
            displayMenu: show,
        })
    }
}

function select(state) {
    return {
        user: state.user,
    }
}

// Wrap the component to inject dispatch and state into it
export default connect(select)(UserNavContainer)

最后是使用 UserNavContainer 的顶级组件:

class AppHandler extends Component {
    componentWillMount() {
        authUser(this.authSuccess.bind(this), this.authFail.bind(this))
    }

    authSuccess() {
        this.props.dispatch(login())
    }

    authFail() {
        this.props.dispatch(logout())
    }

    render() {
        window.console.log("renderrenderrenderrenderrenderrenderrenderrenderrenderrender");
        return (
            <div className="app-container">
            <div className="top-nav row">
                <div className="col-md-8 col-md-offset-2 col-sm-10 col-sm-offset-1 col-xs-12">
                    <BackButton
                        pathname={this.props.location.pathname}
                    />
                    <UserNavContainer
                        pathname={this.props.location.pathname}
                    />
                    <div className="header">
                        <img src="https://www.wewherego.com/img/logo/logo_wherego.png"/>
                        <h1>{this.props.pageHeader}</h1>
                    </div>
                </div>
            </div>
            <ReactCSSTransitionGroup
                transitionName="example"
                transitionEnterTimeout={1000}
                transitionLeaveTimeout={1000}
            >
                {React.cloneElement(this.props.children, {
                    key: this.props.location.pathname,
                })}
            </ReactCSSTransitionGroup>
            </div>
        )
    }
}

function select(state) {
    return {
        selectedCity: state.selectedCity,
        pageHeader: state.pageHeader,
    }
}

// Wrap the component to inject dispatch and state into it
export default connect(select)(AppHandler)

在AppHandler中,它调用了ComponentWillMount中的以下方法:

导出函数 authUser(loginCb, logoutCb) { const data = readCookie('something')

if (!data) {
    logoutCb()
} else {
    loginCb()
}
return

}

导出函数signoutUser() { //清理一些东西 }

当我打开页面时,我只能看到 1 行 渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染渲染

但我看到日志一直在打印:

UserNavContainerUserNavContainerUserNavContainerUserNavContainerUserNavContainerUserNavContainer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
1057
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
1058
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
1059
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

显然,由于某种原因,componentWillMount 方法一直被调用,而 getMyProfile() 中的 a 现在变为 1059(并且仍在计数)。

我不知道为什么会这样?

【问题讨论】:

  • 我认为您也应该发布渲染组件的位置。容器组件可能有问题。
  • @BruceMu 我已经更新了更多细节。

标签: javascript reactjs redux


【解决方案1】:

如果你在组件生命周期方法中调度动作,你应该在componentDidMount而不是 willMount 中进行。看来您正在触发阻止组件安装的无限循环。

【讨论】:

  • 我尝试使用 componentDidMount 但它仍然会出现同样的问题。
【解决方案2】:

尝试将您的 onClick 函数调用包装为-

 onClick={() => user.toggleMenu}

【讨论】:

    【解决方案3】:

    真正的问题是,在组件中,它们正在调度 SET_USER_PROFILE 事件,这会将 loggedIn 变量更改为 true,但随后应用会调度 GET_MY_PROFILE_START,这会将 loggedIn 变量更改为 false。这反过来会触发新循环并将loggedIn 设置为true,因此循环继续进行。

    正确的做法是在reducer中,不用设置变量,而是使用object.assign来改变变量的值。所以它真的应该是:

    export default (state = {
        loggedIn: false,
        profiledLoading: false,
        profile: {},
    }, action) => {
        switch (action.type) {
            case GET_MY_PROFILE_START:
                return Object.assign({}, state, {
                    loggedIn: true,
                    profiledLoading: true,
                })
            case CLEAR_USER_PROFILE:
                return Object.assign({}, state, {
                    loggedIn: false,
                })
            case GET_MY_PROFILE_ERROR:
                return Object.assign({}, state, {
                    profiledLoaded: false,
                    profile: {},
                })
            case GET_MY_PROFILE_SUCCESS:
                return Object.assign({}, state, {
                    profiledLoading: false,
                    profile: action.profile,
                })
            default:
                return state
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2016-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-19
      • 2019-02-01
      • 2020-01-07
      相关资源
      最近更新 更多