reactjs - Ionic/React/TypeScript,使用react-router history.push, history.replace and history.goBack动画触发两次

标签 reactjs typescript ionic-framework react-router ionic-react

所以基本上我正在开发一个 Ionic/React Typescript 应用程序,当我导航到应用程序时,这个奇怪的页面转换发生了两次(见下面的 gif)

我检查了一下,不是渲染调用了两次,因为 componentWillMount/didMount/willUnmount 都触发一次,就像推送页面和推送页面一样。

如您所见,转换总是发生两次并且在任何地方都找不到解决方案...... 使用的版本: - ionic :5.2.1 - react :16.9.0(npm 包) - typescript 3.6.3(npm 包)


Here is the code for the page with the "Diplomes" Title:
class Diplome extends React.Component<RouteComponentProps> {

  // Function who redirect to the url for edit an situation
  redirectUrlToEditSituation = () => {
    this.props.history.push('/app/edit/diplome/', null);
  }

  render() {
    return (
      <IonPage>
          <IonHeader>
            <IonToolbar>
                <ButtonHambToolBar />
                <ToolBarLogo toolbarName={ToolBarName.DIPLOME}/>
            </IonToolbar>
          </IonHeader>
          <IonContent>
            <FabButtonAction ClickHandler={() => this.redirectUrlToEditSituation()} icon={add}/>
            <GenericList type={ModelType.DIPLOME}/>
          </IonContent>
      </IonPage>
    );
  }
}

export default (Diplome);

这里是单击按钮时带有“版本”标题的页面:

class DiplomeEdit extends Component<DiplomeEditProps, {
    dataIsLoad: boolean,
    label_field: string,
    isAnCreate: boolean,
    openModalDelete: boolean,
    isValidation: boolean,
    currentDiplome: any
}>
{

    constructor(props: DiplomeEditProps) {
        super(props);
        this.state = {
            label_field: '',
            isAnCreate: true,
            openModalDelete: false,
            dataIsLoad: false,
            currentDiplome: '',
            isValidation: false,
        };
    }

    async componentWillMount() {
        console.log("component will mount");
        await this.getCurrentDiplomeToUpdate();
    }

    // Function who check if they are param on the url
    // If param exist get the current diplome on the store
    getCurrentDiplomeToUpdate = async () => {
        if (this.props.match.params.idDiplome !== undefined) {

            const diplomes: DiplomeInterface[] = this.props.diplomes;
            if (diplomes.length > 0) {
                const currentDiplomeReceive: DiplomeInterface | undefined = diplomes.find((res: DiplomeInterface) => {
                    return this.props.match.params.idDiplome === res.idDiplome;
                });

                if (currentDiplomeReceive !== undefined) {
                    this.setState({
                        isAnCreate: false,
                        label_field: currentDiplomeReceive.labelDiplome,
                        currentDiplome: currentDiplomeReceive,
                        dataIsLoad: true,
                    });
                }
            }

        } else {
            this.setState({
                isAnCreate: true,
                dataIsLoad: true,
            });
        }
    }

    // Function who render update button
    renderUpdateButton = () => {
        return (
            <div className="contenaire_button_action_situation_edit">
                <Button hidden={this.state.isAnCreate}
                    onClick={() => this.openOrCloseModalDeleteDialog()}
                    className="button_delete_situation">Supprimer</Button>
                <Button
                    onClick={e => this.actionClick()}
                    className="button_action_situation_edit">Enregistrer</Button>
            </div>
        );
    }

    // Function who delete diplome on the databse and on the store
    removeDiplome = async () => {
        await REQUEST.deleteDiplome(this.state.currentDiplome.idDiplome);
        store.dispatch(DELETE_DIPLOME(this.state.currentDiplome.idDiplome));
        this.props.history.replace('/app/diplomes');
    }

    // Function who get the label and id for create new diplome on the databae
    // Reset the state to the default init
    saveDiplome = async () => {
        const obj: DiplomeInterface = {
            idDiplome: Math.random().toString(36).substr(2, 9),
            labelDiplome: this.state.label_field.toLowerCase(),
        };
        this.setState({
            label_field: '',
            isAnCreate: false,
            openModalDelete: false,
        });
        await REQUEST.postDiplome(obj);
        store.dispatch(ADD_DIPLOME(obj));
        //this.props.history.replace('/app/diplomes');
        window.smartAlert("Diplome ajouté avec succès", "success", 5000);
        this.props.history.goBack();
    }

    // Function who update a diplome on the store
    // Need the current diplome
    // Label update
    updateDiplome = async () => {
        const currentDiplomeReceive: DiplomeInterface = this.state.currentDiplome;

        const obj: DiplomeInterface = {
            idDiplome: currentDiplomeReceive.idDiplome,
            labelDiplome: this.state.label_field,
        };

        if (this.props.userConnected.typeAccount === TypeConnect.ADMIN) {
            await REQUEST.updateDiplome(obj);
            store.dispatch(UPDATE_DIPLOME(obj));
            this.props.history.replace('/app/diplomes');
        }
    }

    // Function who checked what action we need
    actionClick = () => {
        if (this.state.isAnCreate) {
            this.saveDiplome();
        } else {
            this.updateDiplome();
        }
    }

    // Function call when input change
    inputChange = (e: any) => {
        this.setState({
            label_field: e.target.value,
        });
    }

    // Function who change the openModalDelete to true
    openOrCloseModalDeleteDialog = () => {
        if (this.props.userConnected.typeAccount === TypeConnect.ADMIN) {
            this.setState({
                openModalDelete: !this.state.openModalDelete,
            });
        }
    }

    render() {

        return (
            <IonPage>
                <IonHeader>
                    <IonToolbar className="task_cat_toolbar">
                        <IonBackButton
                            className="situation_edit_back_button" />
                        <ToolBarLogo toolbarName={ToolBarName.EDIT} />
                    </IonToolbar>
                </IonHeader>

                <IonContent>
                    <div className="contenaire_edit">
                        <div className="contenaire_form_situation">
                            <div className="contenaire_field">
                                <TextField
                                    onChange={e => this.inputChange(e)}
                                    value={this.state.label_field}
                                    className="field_form_situation"
                                    label="Libelle" />
                            </div>

                            <div className="container_task_situation_edit">

                                {!this.state.dataIsLoad && <SpinnerCircular />}

                                {this.renderUpdateButton()}

                                {
                                    this.state.openModalDelete &&
                                    <DialogDelete
                                        actionRemove={() => this.removeDiplome()}
                                        open={this.state.openModalDelete}
                                        actionCloseModal={() => this.openOrCloseModalDeleteDialog()} />
                                }

                            </div>
                        </div>
                    </div>
                </IonContent>

            </IonPage>
        );
    }

}

const mapStateToProps = (state: DiplomeEditProps) => ({
    diplomes: state.diplomes,
    network: state.network,
    role: state.role,
    userConnected: state.userConnected,
});

export default connect(mapStateToProps)(DiplomeEdit);

最佳答案

您不能将 componentWillMount 生命周期方法声明为异步。

来自React docs about UNSAFE_componentWillMount()

Avoid introducing any side-effects or subscriptions in this method.
For those use cases, use componentDidMount() instead.

React blog "Update on Async Rendering" (03/2018):

One of the biggest lessons we’ve learned is that some of our legacy component
lifecycles tend to encourage unsafe coding practices. They are:

    componentWillMount
    componentWillReceiveProps
    componentWillUpdate

These lifecycle methods have often been misunderstood and subtly misused; 
furthermore, we anticipate that their potential misuse may be more problematic
with async rendering. Because of this, we will be adding an “UNSAFE_” prefix
to these lifecycles in an upcoming release. (Here, “unsafe” refers not to 
security but instead conveys that code using these lifecycles will be more 
likely to have bugs in future versions of React, especially once async 
rendering is enabled.)

Victoria Fluharty 撰写了有用的帖子 componentWillMount() vs componentDidMount()关于那个话题。

tl;dr:如果您有异步逻辑,则需要将其移至 componentDidMount()(或迁移至更现代的方法,例如使用 React hooks 的功能组件|而不是部分弃用的生命周期方法。

您可以将 componentDidMount() 声明为 async,但可能会有一些注意事项:

关于reactjs - Ionic/React/TypeScript,使用react-router history.push, history.replace and history.goBack动画触发两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62298959/

相关文章:

javascript - 如何防止 github 在我的 React 代码中突出显示撇号

arrays - typescript array.map 丢失返回类型

javascript - Ionic 与谷歌分析

angular - Ionic 2 rc0 应用程序的 ionic 服务失败

javascript - 如何使用 Ionic 和 AngularJS 获取 radio 输入值?

javascript - 未捕获(在 promise 中)

javascript - 延迟 componentDidMount 获取 this.props 被读取

javascript - 不在焦点时隐藏文本区域字段

Angular 14 - 错误 : Trying to publish a package that has been compiled by Ivy in full compilation mode

javascript - Reducer 不知道我在 React Redux 中的状态