我试图调用后端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,
}
}
并关注reducer:
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中调用以下方法:
export function authUser(loginCb,logoutCb){ const data = readCookie('something')
if (!data) {
logoutCb()
} else {
loginCb()
}
return
}
export function signoutUser(){ //清理一些东西 }
当我打开页面时,我只能看到1行 renderrenderrenderrenderrenderrenderrenderrenderrenderrenderrender
但我看到日志不断打印:
UserNavContainerUserNavContainerUserNavContainerUserNavContainerUserNavContainerUserNavContainer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
1057
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
1058
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
1059
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
显然,由于某种原因,componentWillMount方法被保持被调用,而getMyProfile()中的a现在变为1059(并且仍在计数中)。
我不知道为什么会这样?
答案 0 :(得分:0)
如果您要在组件生命周期方法中调度操作,则应在componentDidMount
而不是willMount中执行。您似乎正在触发阻止组件安装的无限循环。
答案 1 :(得分:0)
尝试将onClick函数调用包装为 -
onClick={() => user.toggleMenu}
答案 2 :(得分:0)
真正的问题是,在组件中,它们调度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
}
}