我正在尝试下面的useEffect示例:
useEffect(async () => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
const json = await response.json();
setPosts(json.data.children.map(it => it.data));
} catch (e) {
console.error(e);
}
}, []);
我在控制台得到这个警告。但我认为,对于异步调用,清理是可选的。我不知道为什么我得到这个警告。链接沙盒为例。https://codesandbox.io/s/24rj871r0p
正确执行并避免错误:“警告:无法在未挂载的…上执行React状态更新…”
useEffect(() => {
let mounted = true;
(async () => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
const json = await response.json();
const newPosts = json.data.children.map(it => it.data);
if (mounted) {
setPosts(newPosts);
}
} catch (e) {
console.error(e);
}
})();
return () => {
mounted = false;
};
}, []);
或外部函数和使用对象
useEffect(() => {
let status = { mounted: true };
query(status);
return () => {
status.mounted = false;
};
}, []);
const query = async (status: { mounted: boolean }) => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
const json = await response.json();
const newPosts = json.data.children.map(it => it.data);
if (status.mounted) {
setPosts(newPosts);
}
} catch (e) {
console.error(e);
}
};
或中止控制器
useEffect(() => {
const abortController = new AbortController();
(async () => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`, { signal: abortController.signal });
const json = await response.json();
const newPosts = json.data.children.map(it => it.data);
setPosts(newPosts);
} catch (e) {
if(!abortController.signal.aborted){
console.error(e);
}
}
})();
return () => {
abortController.abort();
};
}, []);
我通读了这个问题,觉得答案中没有提到实现useEffect的最佳方法。
假设您有一个网络呼叫,并且希望在得到响应后执行一些操作。
为了简单起见,让我们将网络响应存储在一个状态变量中。
人们可能希望使用action/reducer来更新存储与网络响应。
const [data, setData] = useState(null);
/* This would be called on initial page load */
useEffect(()=>{
fetch(`https://www.reddit.com/r/${subreddit}.json`)
.then(data => {
setData(data);
})
.catch(err => {
/* perform error handling if desired */
});
}, [])
/* This would be called when store/state data is updated */
useEffect(()=>{
if (data) {
setPosts(data.children.map(it => {
/* do what you want */
}));
}
}, [data]);
参考=> https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
对于使用React Hooks从外部API中获取,你应该调用一个从useEffect钩子内部的API中获取的函数。
是这样的:
async function fetchData() {
const res = await fetch("https://swapi.co/api/planets/4/");
res
.json()
.then(res => setPosts(res))
.catch(err => setErrors(err));
}
useEffect(() => {
fetchData();
}, []);
我强烈建议你不要在useEffect钩子中定义你的查询,因为它会被无限次地重新渲染。由于不能将useEffect设置为异步,所以可以将其中的函数设置为异步。
在上面所示的例子中,API调用在另一个单独的异步函数中,因此它确保调用是异步的,并且只发生一次。此外,useEffect的依赖数组([])是空的,这意味着它的行为就像React类组件中的componentDidMount一样,它只会在组件被挂载时执行一次。
对于加载文本,你可以使用React的条件渲染来验证你的帖子是否为空,如果是,渲染一个加载文本,否则,显示帖子。当你完成从API获取数据并且post不为空时,else将为真。
{posts === null ? <p> Loading... </p>
: posts.map((post) => (
<Link key={post._id} to={`/blog/${post.slug.current}`}>
<img src={post.mainImage.asset.url} alt={post.mainImage.alt} />
<h2>{post.title}</h2>
</Link>
))}
我看到你已经在使用条件渲染,所以我建议你深入了解它,特别是验证对象是否为空!
如果您需要更多关于使用Hooks使用API的信息,我建议您阅读以下文章。
https://betterprogramming.pub/how-to-fetch-data-from-an-api-with-react-hooks-9e7202b8afcd
https://reactjs.org/docs/conditional-rendering.html
只是一个关于purescript语言如何AWESOME处理这个问题的陈腐效果与Aff单子
没有PURESCRIPT
你必须使用AbortController
function App() {
const [ data, setData ] = React.useState([]);
React.useEffect(() => {
const abortController = new AbortController();
void async function fetchData() {
try {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
const response = await fetch(url, { signal: abortController.signal });
setData(await response.json());
} catch (error) {
console.log('error', error);
}
}();
return () => {
abortController.abort(); // cancel pending fetch request on component unmount
};
}, []);
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
或者陈旧(来自NoahZinsmeister/web3-react的例子)
function Balance() {
const { account, library, chainId } = useWeb3React()
const [balance, setBalance] = React.useState()
React.useEffect((): any => {
if (!!account && !!library) {
let stale = false
library
.getBalance(account)
.then((balance: any) => {
if (!stale) {
setBalance(balance)
}
})
.catch(() => {
if (!stale) {
setBalance(null)
}
})
return () => { // NOTE: will be called every time deps changes
stale = true
setBalance(undefined)
}
}
}, [account, library, chainId]) // ensures refresh if referential identity of library doesn't change across chainIds
...
与PURESCRIPT
检查如何useAff杀死它的Aff在清理功能
Aff被实现为一个状态机(没有承诺)
但与我们相关的是:
你可以把你的AbortController放在这里
如果错误被抛出到光纤或光纤内部,它将停止运行Effects(未测试)和Affs(从第二个例子开始,它将不运行,因此它将not setBalance(balance))
正确执行并避免错误:“警告:无法在未挂载的…上执行React状态更新…”
useEffect(() => {
let mounted = true;
(async () => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
const json = await response.json();
const newPosts = json.data.children.map(it => it.data);
if (mounted) {
setPosts(newPosts);
}
} catch (e) {
console.error(e);
}
})();
return () => {
mounted = false;
};
}, []);
或外部函数和使用对象
useEffect(() => {
let status = { mounted: true };
query(status);
return () => {
status.mounted = false;
};
}, []);
const query = async (status: { mounted: boolean }) => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
const json = await response.json();
const newPosts = json.data.children.map(it => it.data);
if (status.mounted) {
setPosts(newPosts);
}
} catch (e) {
console.error(e);
}
};
或中止控制器
useEffect(() => {
const abortController = new AbortController();
(async () => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`, { signal: abortController.signal });
const json = await response.json();
const newPosts = json.data.children.map(it => it.data);
setPosts(newPosts);
} catch (e) {
if(!abortController.signal.aborted){
console.error(e);
}
}
})();
return () => {
abortController.abort();
};
}, []);