具有 React Suspense 的组件会导致永恒循环
Component with React Suspense cause Eternal Loop
我正在学习 React,并尝试使用 Suspense。
首先,我尝试使用包装的 promise 对象作为道具。
包装器在未解决时抛出承诺。它永远循环。
然后我用useEffect试试,还是一样的问题
这是我的代码和沙箱。
请教我如何解决这个问题。
textsandbox EternalLoop
* useEffect 块注释在沙箱中被淘汰,因为它会永远循环。
import React, { Suspense, useEffect, useState } from "react";
const lazyTimer = () => {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(" from 10sec past");
}, 1000);
});
return promise;
};
const wrapPromise = promise => {
let status = "pending";
let result;
console.log("looping....");
const suspender = promise.then(
r => {
status = "fulfilled";
result = r;
},
e => {
status = "rejected";
result = e;
}
);
const read = () => {
if (status === "pending") {
throw suspender;
} else if (status === "rejected") {
throw result;
} else {
return result;
}
};
return { read };
};
const Hallo = () => {
const [text, setText] = useState("your on time");
// useEffect(
// setText(
// wrapPromise(lazyTimer()).read()
// ), [text],
// );
return <h2>hello world, {text}</h2>;
};
export default function App() {
return (
<div className="App">
<Suspense fallback={<p>Loading...</p>}>
<Hallo />
</Suspense>
</div>
);
}
编辑 3:让我感到困惑的是我没有看到记录的悬念是它是如何工作的。 IE,children 是如何工作的。在我看来,您通过查看文档中的演示代码做出了承诺。但是我没有在任何地方看到这个记录。
所以你抛出一个承诺,当解析时你的组件现在已经准备好了(即定时器承诺 - 或者 Apollo 客户端承诺 - 获取承诺等)。当该承诺解决时,您的组件现在可以挂载。我喜欢它,如果我对它的工作原理是正确的,我希望它被清楚地记录下来。
本题与悬念无关。您的代码:
const Hallo = () => {
const [text, setText] = useState("your on time");
useEffect(
// setText(
// wrapPromise(lazyTimer()).read()
// ), [text],
// );
return <h2>hello world, {text}</h2>;
};
有这个问题。它运行 setText
,然后它具有 text
作为依赖项。所以你改变 text
,然后它再次运行,因为 text
改变了。因此无限循环。
你有 3 种方法来解决这个问题
1) 做一些 if
语句使其不是无限循环(即你知道什么文本不应该是或检查它是否相同)。
2) 从依赖列表中删除 text
(不好!)
3) 使用替代方法 setText
将其删除,将其作为函数调用而不是提供值。有关文档,请参阅 here。
useEffect(
setText(text => wrapPromise(lazyTimer()).read())
), [],
);
那么text
就不是依赖关系了。
我推荐3.
编辑:
最重要的是,您错误地使用了包装器。我看了看tutorial you probably used and its Github。他们创建包装器。然后在数据获取部分(您的计时器)将他们的承诺包装在承诺包装器中。
我尝试使用 useEffect
:
使您的代码尽可能相似
import React, { Suspense, useEffect, useState } from "react";
import "./styles.css";
const wrapPromise = promise => {
let status = "pending";
let result;
console.log(`looping.... + ${new Date().toString()}`);
const suspender = promise.then(
r => {
status = "fulfilled";
result = r;
},
e => {
status = "rejected";
result = e;
}
);
const read = () => {
if (status === "pending") {
throw suspender;
} else if (status === "rejected") {
throw result;
} else {
return result;
}
};
return { read };
};
const lazyTimer = () => {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(" from 10sec past");
}, 10000);
});
return wrapPromise(promise);
};
const data = lazyTimer();
const Hallo = () => {
const [text, setText] = useState(data.read());
useEffect(() => {
console.log("Im running");
setText(data.read());
}, []);
return <h2>hello world, {text}</h2>;
};
export default function App() {
return (
<div className="App">
<Suspense fallback={<p>Loading...</p>}>
<Hallo />
</Suspense>
</div>
);
}
这行得通。沙盒 here.
请注意,光环可能只是:
const Hallo = () => {
const text = data.read()
return <h2>hello world, {text}</h2>;
};
我正在学习 React,并尝试使用 Suspense。
首先,我尝试使用包装的 promise 对象作为道具。
包装器在未解决时抛出承诺。它永远循环。
然后我用useEffect试试,还是一样的问题
这是我的代码和沙箱。 请教我如何解决这个问题。
textsandbox EternalLoop
* useEffect 块注释在沙箱中被淘汰,因为它会永远循环。
import React, { Suspense, useEffect, useState } from "react";
const lazyTimer = () => {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(" from 10sec past");
}, 1000);
});
return promise;
};
const wrapPromise = promise => {
let status = "pending";
let result;
console.log("looping....");
const suspender = promise.then(
r => {
status = "fulfilled";
result = r;
},
e => {
status = "rejected";
result = e;
}
);
const read = () => {
if (status === "pending") {
throw suspender;
} else if (status === "rejected") {
throw result;
} else {
return result;
}
};
return { read };
};
const Hallo = () => {
const [text, setText] = useState("your on time");
// useEffect(
// setText(
// wrapPromise(lazyTimer()).read()
// ), [text],
// );
return <h2>hello world, {text}</h2>;
};
export default function App() {
return (
<div className="App">
<Suspense fallback={<p>Loading...</p>}>
<Hallo />
</Suspense>
</div>
);
}
编辑 3:让我感到困惑的是我没有看到记录的悬念是它是如何工作的。 IE,children 是如何工作的。在我看来,您通过查看文档中的演示代码做出了承诺。但是我没有在任何地方看到这个记录。
所以你抛出一个承诺,当解析时你的组件现在已经准备好了(即定时器承诺 - 或者 Apollo 客户端承诺 - 获取承诺等)。当该承诺解决时,您的组件现在可以挂载。我喜欢它,如果我对它的工作原理是正确的,我希望它被清楚地记录下来。
本题与悬念无关。您的代码:
const Hallo = () => {
const [text, setText] = useState("your on time");
useEffect(
// setText(
// wrapPromise(lazyTimer()).read()
// ), [text],
// );
return <h2>hello world, {text}</h2>;
};
有这个问题。它运行 setText
,然后它具有 text
作为依赖项。所以你改变 text
,然后它再次运行,因为 text
改变了。因此无限循环。
你有 3 种方法来解决这个问题
1) 做一些 if
语句使其不是无限循环(即你知道什么文本不应该是或检查它是否相同)。
2) 从依赖列表中删除 text
(不好!)
3) 使用替代方法 setText
将其删除,将其作为函数调用而不是提供值。有关文档,请参阅 here。
useEffect(
setText(text => wrapPromise(lazyTimer()).read())
), [],
);
那么text
就不是依赖关系了。
我推荐3.
编辑:
最重要的是,您错误地使用了包装器。我看了看tutorial you probably used and its Github。他们创建包装器。然后在数据获取部分(您的计时器)将他们的承诺包装在承诺包装器中。
我尝试使用 useEffect
:
import React, { Suspense, useEffect, useState } from "react";
import "./styles.css";
const wrapPromise = promise => {
let status = "pending";
let result;
console.log(`looping.... + ${new Date().toString()}`);
const suspender = promise.then(
r => {
status = "fulfilled";
result = r;
},
e => {
status = "rejected";
result = e;
}
);
const read = () => {
if (status === "pending") {
throw suspender;
} else if (status === "rejected") {
throw result;
} else {
return result;
}
};
return { read };
};
const lazyTimer = () => {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(" from 10sec past");
}, 10000);
});
return wrapPromise(promise);
};
const data = lazyTimer();
const Hallo = () => {
const [text, setText] = useState(data.read());
useEffect(() => {
console.log("Im running");
setText(data.read());
}, []);
return <h2>hello world, {text}</h2>;
};
export default function App() {
return (
<div className="App">
<Suspense fallback={<p>Loading...</p>}>
<Hallo />
</Suspense>
</div>
);
}
这行得通。沙盒 here.
请注意,光环可能只是:
const Hallo = () => {
const text = data.read()
return <h2>hello world, {text}</h2>;
};