เมื่อคุณส่งคำสัญญาอาจใช้เวลาสองสามวินาทีก่อนที่จะแก้ไขและเมื่อถึงเวลานั้นผู้ใช้อาจนำทางไปยังที่อื่นในแอปของคุณ ดังนั้นเมื่อ Promise แก้ไขsetState
ถูกดำเนินการกับส่วนประกอบที่ไม่ได้ต่อเชื่อมและคุณได้รับข้อผิดพลาดเช่นเดียวกับในกรณีของคุณ นอกจากนี้ยังอาจทำให้หน่วยความจำรั่วไหล
นั่นเป็นเหตุผลที่ดีที่สุดที่จะย้ายตรรกะอะซิงโครนัสบางส่วนของคุณออกจากส่วนประกอบ
มิฉะนั้นคุณจะต้องอย่างใดยกเลิกสัญญาของคุณ อีกทางเลือกหนึ่ง - เป็นเทคนิคสุดท้าย (เป็นแอนตี้แพตเทิร์น) - คุณสามารถเก็บตัวแปรไว้เพื่อตรวจสอบว่าส่วนประกอบยังคงติดตั้งอยู่:
componentDidMount(){
this.mounted = true;
this.props.fetchData().then((response) => {
if(this.mounted) {
this.setState({ data: response })
}
})
}
componentWillUnmount(){
this.mounted = false;
}
ฉันจะเน้นย้ำอีกครั้ง - นี่เป็นรูปแบบการต่อต้านแต่อาจเพียงพอในกรณีของคุณ (เช่นเดียวกับที่ทำกับFormik
การนำไปใช้งาน)
การสนทนาที่คล้ายกันในGitHub
แก้ไข:
นี่อาจเป็นวิธีที่ฉันจะแก้ปัญหาเดียวกัน (ไม่มีอะไรเลยนอกจากตอบสนอง) ด้วยHooks :
ตัวเลือก A:
import React, { useState, useEffect } from "react";
export default function Page() {
const value = usePromise("https://something.com/api/");
return (
<p>{value ? value : "fetching data..."}</p>
);
}
function usePromise(url) {
const [value, setState] = useState(null);
useEffect(() => {
let isMounted = true;
request.get(url)
.then(result => {
if (isMounted) {
setState(result);
}
});
return () => {
isMounted = false;
};
}, []);
return value;
}
ตัวเลือก B:หรืออีกวิธีหนึ่งที่มีuseRef
พฤติกรรมเหมือนคุณสมบัติคงที่ของคลาสซึ่งหมายความว่าจะไม่ทำให้องค์ประกอบแสดงผลเมื่อค่าเปลี่ยนแปลง:
function usePromise2(url) {
const isMounted = React.useRef(true)
const [value, setState] = useState(null);
useEffect(() => {
return () => {
isMounted.current = false;
};
}, []);
useEffect(() => {
request.get(url)
.then(result => {
if (isMounted.current) {
setState(result);
}
});
}, []);
return value;
}
function useIsMounted() {
const isMounted = React.useRef(true)
useEffect(() => {
return () => {
isMounted.current = false;
};
}, []);
return isMounted;
}
ตัวอย่าง: https://codesandbox.io/s/86n1wq2z8