代码之家  ›  专栏  ›  技术社区  ›  Tord Larsen smaznet

如何修复在现有状态转换期间无法更新的错误

  •  0
  • Tord Larsen smaznet  · 技术社区  · 4 年前

    我得到了这个错误:

    无法在现有状态转换期间更新(例如在 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;
    
    0 回复  |  直到 4 年前