我得到了这个错误:
无法在现有状态转换期间更新(例如在
render
).渲染方法应该是道具和
状态
在这段代码中
callback(newDisplayName)
我知道为什么会发生这样的事
提供
不应该像我回电话那样被触发!我试图解决它,但我很困惑。
我读了这个
cannot-update-during-an-existing-state-transition-error-in-react
但我不能像建议的那样使用lambda,比如:
{(newDisplayName) => callback(newDisplayName) }`
但我有一个三元运算,所以不一样,对吧?我可能不需要在渲染中进行回调,但我得到了这个重做
mapStateToProps
更新后触发了渲染,所以不知道还有什么地方可以这样做
import React from 'react';
import '../../styles/change-name.css';
import { connect } from 'react-redux';
import Dots from 'react-activity/lib/Dots';
import 'react-activity/lib/Dots/Dots.css';
import { changeDisplayName } from '../../redux/userData/user.actions';
class ChangeName extends React.Component {
constructor(props) {
super(props);
const { authUser } = this.props;
this.state = {
displayName: authUser.displayName ?? '',
};
}
onSubmit = event => {
event.preventDefault();
this.updateUserName();
};
onChange = event => {
this.setState({ [event.target.name]: event.target.value });
};
updateUserName() {
const { displayName } = this.state;
const { changeUserDisplayName } = this.props;
changeUserDisplayName(displayName.trim());
}
render() {
const { displayName } = this.state;
const { callback } = this.props;
const { authUser, savingDisplayName, newDisplayName, changeDisplayNameErr } = this.props;
const isInvalid = !displayName || !displayName.trim() || displayName.trim().length > 20;
const isAmended = displayName && displayName.trim() !== authUser.displayName;
return (
<div>
<div className="changename">
<form onSubmit={this.onSubmit}>
<input
name="displayName"
value={displayName}
onChange={this.onChange}
type="text"
maxLength="20"
placeholder="Display name"
/>
<button className="changenamebutton" disabled={isInvalid || !isAmended} type="submit">
Confirm
</button>
{isInvalid && <p className="error">Display name must be between 1 and 20 characters in length</p>}
{changeDisplayNameErr && <p className="error">{changeDisplayNameErr.message}</p>}
{newDisplayName && <p className="error">New name was saved!</p>}
{newDisplayName ? callback(newDisplayName) : ''}
</form>
</div>
<div>{savingDisplayName ? <Dots /> : null}</div>
</div>
);
}
}
const mapDispatchToProps = dispatch => ({
changeUserDisplayName: displayName => dispatch(changeDisplayName(displayName)),
});
const mapStateToProps = state => {
return {
savingDisplayName: state.user.isSavingDisplayName,
newDisplayName: state.user.displayName,
changeDisplayNameErr: state.user.changeDisplayNameErrMsg,
};
};
export default connect(mapStateToProps, mapDispatchToProps)(ChangeName);
以下是回调的母组件:
import React from 'react';
import '../../styles/profile-page-anonymous.css';
import { compose } from 'recompose';
import { withFirebase } from '../../firebase';
import { AuthUserContext, withAuthorization } from '../../session';
import ChangeName from './ChangeName';
import * as ROLES from '../../constants/roles';
class ProfilePageBase extends React.Component {
constructor(props) {
super(props);
this.state = {
displayName: props.authUser.displayName ?? '',
};
this.nameChange = this.nameChange.bind(this);
}
nameChange(displayName) {
this.setState({
displayName,
});
}
render() {
let userDetails;
const { authUser } = this.props;
const { displayName } = this.state;
if (authUser) {
userDetails = (
<div>
<div className="profileAnonymous">
<table>
<tbody>
<tr>
<td>Display name: </td>
<td>{displayName}</td>
</tr>
<tr>
<td>User ID: </td>
<td>{authUser.uid}</td>
</tr>
<tr>
<td>Anonymous: </td>
<td>{authUser.isAnonymous ? 'True' : 'False'}</td>
</tr>
</tbody>
</table>
</div>
<div>
<h2>Change your display name</h2>
</div>
<div className="profileBoxChangeNameAnonymous">
<ChangeName authUser={authUser} callback={this.nameChange} />
</div>
</div>
);
} else {
userDetails = <p>Unable to get user details. Please try refreshing the page.</p>;
}
return <div>{userDetails}</div>;
}
}
const ProfilePageAnon = props => (
<AuthUserContext.Consumer>
{authUser => (
<div className="profilePageAnonymous">
<ProfilePageBase {...props} authUser={authUser} />
</div>
)}
</AuthUserContext.Consumer>
);
const condition = authUser => authUser && authUser.roles.includes(ROLES.ANON);
const enhance = compose(withAuthorization(condition), withFirebase);
const ProfilePageAnonymous = enhance(ProfilePageAnon);
export default ProfilePageAnonymous;
使现代化
重构
ProfilePageBase
解决问题!多亏了@drewerese。我了解了反模式,即在组件状态下不存储通过的道具。现在我重构
ProfilePageBase
转换为无状态组件。
ProfilePageBase
是一个
<AuthUserContext.Consumer>
监听用户对象上的Firestore会发生变化
ChangName
然后将新名称保存到Firestore
ProfilePageBase
消耗
AuthUserContext
HOC渲染
value
这是具有新名称的用户对象。所以现在
昌名
不需要考虑/了解
callback
import React from 'react';
import '../../styles/profile-page-anonymous.css';
import { compose } from 'recompose';
import { withFirebase } from '../../firebase';
import { AuthUserContext, withAuthorization } from '../../session';
import ChangeName from './ChangeName';
import * as ROLES from '../../constants/roles';
function ProfilePageBase({ authUser }) {
return (
<div>
{authUser ? (
<div>
<div className="profileAnonymous">
<table>
<tbody>
<tr>
<td>Display name: </td>
<td>{authUser.displayName}</td>
</tr>
<tr>
<td>User ID: </td>
<td>{authUser.uid}</td>
</tr>
<tr>
<td>Anonymous: </td>
<td>{authUser.isAnonymous ? 'True' : 'False'}</td>
</tr>
</tbody>
</table>
</div>
<div>
<h2>Change your display name</h2>
</div>
<div className="profileBoxChangeNameAnonymous">
<ChangeName authUser={authUser} />
</div>
</div>
) : (
<p>Unable to get user details. Please try refreshing the page.</p>
)}
</div>
);
}
const ProfilePageAnon = props => (
<AuthUserContext.Consumer>
{authUser => (
<div className="profilePageAnonymous">
<ProfilePageBase {...props} authUser={authUser} />
</div>
)}
</AuthUserContext.Consumer>
);
const condition = authUser => authUser && authUser.roles.includes(ROLES.ANON);
const enhance = compose(withAuthorization(condition), withFirebase);
const ProfilePageAnonymous = enhance(ProfilePageAnon);
export default ProfilePageAnonymous;