Не удается выполнить обновление состояния реакции на ошибку неустановленного компонента - PullRequest
0 голосов
/ 26 марта 2020

Вскоре я начну с того, что скажу, что я занимался этим часами, и чувствую, что перепробовал все там. Я также пропустил много кода из моего компонента, который, я считаю, не имеет отношения к проблеме. Пользователь увидит этот компонент EditEpi c, когда попытается отредактировать объект epi c (запись в дБ). Здесь пользователь также может удалить объект из БД, что я и пытаюсь сделать сейчас. После удаления пользователь вернется на предыдущую страницу. Вот где я получаю проблему. Вот код

interface IEditEpic extends RouteComponentProps<{
    id: string;
}> {
}

interface IProps {
    updater?: (val:number) => void;
}

export const EditEpic: React.FC<IEditEpic> = ({match}, props:IProps) => {
    const [showAlert2, setShowAlert2] = useState(false);
    const [showAlert3, setShowAlert3] = useState(false);
    const [showAlert4, setShowAlert4] = useState(false);
    const [showAlert5, setShowAlert5] = useState(false);
    const [showAlert6, setShowAlert6] = useState(false);
    const [showAlert7, setShowAlert7] = useState(false);
    const [showAlert8, setShowAlert8] = useState(false);
    const [showAlert9, setShowAlert9] = useState(false);
    const [showAlert10, setShowAlert10] = useState(false);
    const [showAlert11, setShowAlert11] = useState(false);
    const [showAlert12, setShowAlert12] = useState(false);
    const [resultMessage, setResultMessage] = useState();
    const [epic, setEpic] = useState<any>();
    const [pet, setPet] = useState({petPoints: 0, id: ""});
    const history = useHistory();
    const [triggerRefresh, setTriggerFresh] = useState(0);

    useEffect(() => {
        let isSubscribed:boolean = true;
        if (isSubscribed) {
            (async () => {
                const res = await findEpic(match.params.id);
                if (res){
                    console.log("found epic ", res[0]);
                    setEpic(res[0]);
                }
                // set pet
                var my_pet:any = await getPet();
                if (my_pet)
                    if (my_pet.length > 0) {
                        setPet({petPoints: my_pet[0].points, id: my_pet[0]._id});
                    }
            })();
        }
        return () => {isSubscribed = false};
    }, [pet.petPoints]);


    const removeEpic = () => {
        (async () => {
            const result = await deleteEpic(epic);
            if (result === DELETE_EPIC_RESULT.pass)
            {
                console.log("Successful delete");
                setShowAlert7(true);
                console.log("going back in history");
                history.goBack();
            }
            else if (result === DELETE_GOALS_IN_EPIC_RESULT.fail)
                setShowAlert11(true);
            else if (result === DELETE_EPIC_RESULT.id_error)
                setShowAlert12(true);
            else
                setShowAlert6(true);
        })();
    };

    console.log("MY props updater", props.updater);

    return (
        <IonPage>
            <IonContent>
                <IonHeader>
                    <IonToolbar>
                        <IonButtons slot="start">
                            <IonBackButton defaultHref="/home" icon="buttonIcon"/>
                        </IonButtons>
                    </IonToolbar>
                </IonHeader>
                <IonList>
                    <div style={{display: "flex", justifyContent: "center"}}>
                        <h1 style={{fontWeight: "bold", textDecoration: "underline"}}>Edit Epic</h1>
                    </div>
                    <br/>
                    <IonItem>
                        <IonInput
                            value={epic?.epicTitle}
                            placeholder="Title"
                            required={true}
                            debounce={750}
                            clearInput={true}
                            minlength={1}
                            maxlength={50}
                            // onIonChange={e => setTitle(e.detail.value!)}
                            onIonChange={e => setEpic({...epic, epicTitle: e.detail.value!})}
                        >
                        </IonInput>
                    </IonItem>
                    <br/>
                    <br/>
                    <br/>
                    <IonItem>
                        <IonLabel position="floating">Description</IonLabel>
                        <IonTextarea
                            value={epic?.epicDescription}
                            placeholder="Please enter your description here"
                            onIonChange={e => setEpic({...epic, epicDescription: e.detail.value!})}>
                        </IonTextarea>
                    </IonItem>
                    <br/>
                    <br/>
                    <DateTimePicker dateType={DATE_ENUMS.start} setDate={setEpic} goalState={epic}/>
                    <br/>
                    <br/>
                    <DateTimePicker dateType={DATE_ENUMS.end} setDate={setEpic} goalState={epic}/>
                    <br/>
                    <br/>
                    <IonFab horizontal="end" vertical="top" slot="fixed">
                        <IonFabButton color="danger" onClick={() => setShowAlert5(true)}>
                            <IonIcon icon={trashOutline}/>
                        </IonFabButton>
                    </IonFab>
                    <IonAlert
                        isOpen={showAlert6}
                        onDidDismiss={() => setShowAlert6(false)}
                        header={'Error'}
                        message={DELETE_EPIC_RESULT.error}
                        buttons={["OK"]}
                    />
                    <IonAlert
                        isOpen={showAlert7}
                        onDidDismiss={() => setShowAlert7(false)}
                        header={'Success'}
                        message={DELETE_EPIC_RESULT.pass}
                        buttons={["OK"]}
                    />
                    <IonAlert
                        isOpen={showAlert11}
                        onDidDismiss={() => setShowAlert11(false)}
                        header={'Error'}
                        message={DELETE_GOALS_IN_EPIC_RESULT.fail}
                        buttons={["OK"]}
                    />
                    <IonAlert
                        isOpen={showAlert12}
                        onDidDismiss={() => {
                            setShowAlert12(false);
                            setTriggerFresh(triggerRefresh+ 1);
                        }}
                        header={'Error'}
                        message={DELETE_EPIC_RESULT.id_error}
                        buttons={["OK"]}
                    />
                    <IonAlert
                        isOpen={showAlert5}
                        onDidDismiss={() => {
                            setShowAlert5(false);
                            // close modal if the insert was successful
                        }}
                        header={'Delete Epic'}
                        message={"Are you sure that you would like to delete the selected epic? " +
                        "Goals associated with the epic will be deleted as well."}
                        buttons={[{
                            text: "NO",
                            handler: () => {
                                // do nothing
                            }
                        }, {
                            text: "YES",
                            handler: () => {
                                removeEpic();
                            }
                        }]}
                    />
                </IonList>
            </IonContent>
        </IonPage>
    )
};

export default EditEpic;

Здесь важен последний тег IonAlert в том, что возвращает мой компонент. В обработчике кнопок для оповещения я запускаю функцию removeEpi c (), если пользователь нажимает «ДА». Эта функция удаляет объект epi c в базе данных, и я хочу, чтобы go вернулся на последнюю страницу в истории, так как пользователь просто удалил то, на что он смотрел. Когда я закомментировал history.goBack (), код работает нормально.

Я думал, что проблема может быть в том, что history.goBack () был вызван внутри асинхронной c функции, возможно, так как эта функция все еще выполнялась, и я возвращался на страницу, реакция была бы дай мне эту ошибку. Однако комментирование goBack () из removeEpi c () и размещение его после моего вызова removeEpi c () в IonAlert также не сработало. Я не уверен, что еще попробовать, кроме этого. Я попробовал некоторые вещи очистки в useEffect, но ничего не получалось. Любая помощь будет оценена. При необходимости я могу предоставить больше информации.

Кроме того, моя трассировка стека выглядит так

index.js:1 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
    in EditEpic (created by Context.Consumer)
    in Route (at App.tsx:66)
    in View (created by StackManagerInner)

1 Ответ

1 голос
/ 26 марта 2020

Вот что я бы сделал:

  1. добавьте const isMounted = useRef(true) поверх вашего EditEpic компонента
  2. есть еще один (дополнительный) useEffect, который будет выглядеть следующим образом :
useEffect(() => { return () => { isMounted.current = false} }, [])
go бросил каждую строку кода, где у вас есть setSomething.. и добавить проверку isMounted.current && setSomething..

Идея состоит в том, что с useRef вы отслеживаете, установлен компонент или нет, и чем Обновление состояния только если компонент смонтирован.

...