从异步等待中保存 object 时无限循环
Inifinite loop when saving an object from async await
当我在 async/await 操作中创建并 object 时...
export const getData = async datas => {
const a1 = await getData1(datas);
return { a1 };
};
...然后用useState保存...
import { useState, useEffect } from "react";
import { getData } from "./getData";
export const useData = ababab => {
const [data, setData] = useState();
useEffect(() => {
const loadData = async () => {
const newData = await getData(ababab);
setData(newData);
};
console.log(Date.now().toString());
loadData();
}, [ababab]);
return data;
};
...我得到一个无限循环。我不明白。
如果您注释掉 setData - 它不会循环。
如果你return只是a1,它不会循环。
这里是使用useData
的地方:
import React from "react";
import "./styles.css";
import { useAbabab } from "./usaAbabab";
import { useData } from "./useData";
export default function App() {
const ababab = useAbabab();
const data = useData(ababab);
return (
<div className="App">
<h1>Hello CodeSandbox {data && data.a1}</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
这里是useAbabab
的内容:
import { useState } from "react";
export const useAbabab = () => {
const [aaa, setAaa] = useState(0);
const [bbb, setBbb] = useState(5);
return { aaa, bbb, setAaa, setBbb };
};
您可能已经了解到,无限循环是由 useData
中的 useEffect
引起的,它是由 ababab
的更改触发的(可以通过删除 ababab
来自依赖数组)。
虽然 ababab
实际上只是一个对象中的两个完整 useState
输出,但对象本身在每次渲染时都会重新定义,从而触发 useEffect
到 运行。
我认为解决此问题的最简单方法是将 useAbabab
的 return 值包装在 useMemo
中,如下所示:
import { useState, useMemo } from "react";
export const useAbabab = () => {
const [aaa, setAaa] = useState(0);
const [bbb, setBbb] = useState(5);
return useMemo(() => ({ aaa, bbb, setAaa, setBbb }), [aaa, bbb]);
};
由于 ababa
变量名称,很难准确判断您的代码在做什么,但从我在您的代码中读到的内容来看,您似乎想要一个围绕异步资源的通用挂钩-
const identity = x => x
const useAsync = (runAsync = identity, deps = []) => {
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const [result, setResult] = useState(null)
useEffect(_ => {
Promise.resolve(runAsync(...deps))
.then(setResult, setError)
.finally(_ => setLoading(false))
}, deps)
return { loading, error, result }
}
使用我们的自定义钩子 usAsync
看起来像这样 -
function App() {
const ababab =
useAbabab()
const { loading, error, result } =
useAsync(getData, [ababab]) // async function, args to function
if (loading)
return <p>Loading...</p>
if (error)
return <p>Error: {error.message}</p>
return <div>Got data: {result}</div>
}
useAsync
是一个通用的通用钩子,可以专门用于其他有用的方式 -
const fetchJson = (url = "") =>
fetch(url).then(r => r.json()) // <-- stop repeating yourself
const useJson = (url = "") =>
useAsync(fetchJson, [url]) // <-- useAsync
const MyComponent = ({ url = "" }) => {
const { loading, error, result } =
useJson(url) // <-- dead simple
if (loading)
return <pre>loading...</pre>
if (error)
return <pre className="error">error: {error.message}</pre>
return <pre>result: {result}</pre>
}
ReactDOM.render(
<MyComponent url="https://httpbin.org/get?foo=bar" />,
document.body
)
运行 下面的代码片段可以看到 useAsync
和 useJson
在您自己的浏览器中工作 -
const { useState, useEffect } =
React
// fake fetch slows response down so we can see loading
const _fetch = (url = "") =>
fetch(url).then(x =>
new Promise(r => setTimeout(r, 2000, x)))
const identity = x => x
const useAsync = (runAsync = identity, deps = []) => {
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const [result, setResult] = useState(null)
useEffect(_ => {
Promise.resolve(runAsync(...deps))
.then(setResult, setError)
.finally(_ => setLoading(false))
}, deps)
return { loading, error, result }
}
const fetchJson = (url = "") =>
_fetch(url).then(r => r.json())
const useJson = (url = "") =>
useAsync(fetchJson, [url])
const MyComponent = ({ url = "" }) => {
const { loading, error, result } =
useJson(url)
if (loading)
return <pre>loading...</pre>
if (error)
return <pre style={{color: "tomato"}}>error: {error.message}</pre>
return <pre>result: {JSON.stringify(result, null, 2)}</pre>
}
const MyApp = () =>
<main>
ex 1 (success):
<MyComponent url="https://httpbin.org/get?foo=bar" />
ex 2 (error):
<MyComponent url="https://httpbin.org/status/500" />
</main>
ReactDOM.render(<MyApp />, document.body)
pre {
background: ghostwhite;
padding: 1rem;
white-space: pre-wrap;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
当我在 async/await 操作中创建并 object 时...
export const getData = async datas => {
const a1 = await getData1(datas);
return { a1 };
};
...然后用useState保存...
import { useState, useEffect } from "react";
import { getData } from "./getData";
export const useData = ababab => {
const [data, setData] = useState();
useEffect(() => {
const loadData = async () => {
const newData = await getData(ababab);
setData(newData);
};
console.log(Date.now().toString());
loadData();
}, [ababab]);
return data;
};
...我得到一个无限循环。我不明白。 如果您注释掉 setData - 它不会循环。 如果你return只是a1,它不会循环。
这里是使用useData
的地方:
import React from "react";
import "./styles.css";
import { useAbabab } from "./usaAbabab";
import { useData } from "./useData";
export default function App() {
const ababab = useAbabab();
const data = useData(ababab);
return (
<div className="App">
<h1>Hello CodeSandbox {data && data.a1}</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
这里是useAbabab
的内容:
import { useState } from "react";
export const useAbabab = () => {
const [aaa, setAaa] = useState(0);
const [bbb, setBbb] = useState(5);
return { aaa, bbb, setAaa, setBbb };
};
您可能已经了解到,无限循环是由 useData
中的 useEffect
引起的,它是由 ababab
的更改触发的(可以通过删除 ababab
来自依赖数组)。
虽然 ababab
实际上只是一个对象中的两个完整 useState
输出,但对象本身在每次渲染时都会重新定义,从而触发 useEffect
到 运行。
我认为解决此问题的最简单方法是将 useAbabab
的 return 值包装在 useMemo
中,如下所示:
import { useState, useMemo } from "react";
export const useAbabab = () => {
const [aaa, setAaa] = useState(0);
const [bbb, setBbb] = useState(5);
return useMemo(() => ({ aaa, bbb, setAaa, setBbb }), [aaa, bbb]);
};
由于 ababa
变量名称,很难准确判断您的代码在做什么,但从我在您的代码中读到的内容来看,您似乎想要一个围绕异步资源的通用挂钩-
const identity = x => x
const useAsync = (runAsync = identity, deps = []) => {
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const [result, setResult] = useState(null)
useEffect(_ => {
Promise.resolve(runAsync(...deps))
.then(setResult, setError)
.finally(_ => setLoading(false))
}, deps)
return { loading, error, result }
}
使用我们的自定义钩子 usAsync
看起来像这样 -
function App() {
const ababab =
useAbabab()
const { loading, error, result } =
useAsync(getData, [ababab]) // async function, args to function
if (loading)
return <p>Loading...</p>
if (error)
return <p>Error: {error.message}</p>
return <div>Got data: {result}</div>
}
useAsync
是一个通用的通用钩子,可以专门用于其他有用的方式 -
const fetchJson = (url = "") =>
fetch(url).then(r => r.json()) // <-- stop repeating yourself
const useJson = (url = "") =>
useAsync(fetchJson, [url]) // <-- useAsync
const MyComponent = ({ url = "" }) => {
const { loading, error, result } =
useJson(url) // <-- dead simple
if (loading)
return <pre>loading...</pre>
if (error)
return <pre className="error">error: {error.message}</pre>
return <pre>result: {result}</pre>
}
ReactDOM.render(
<MyComponent url="https://httpbin.org/get?foo=bar" />,
document.body
)
运行 下面的代码片段可以看到 useAsync
和 useJson
在您自己的浏览器中工作 -
const { useState, useEffect } =
React
// fake fetch slows response down so we can see loading
const _fetch = (url = "") =>
fetch(url).then(x =>
new Promise(r => setTimeout(r, 2000, x)))
const identity = x => x
const useAsync = (runAsync = identity, deps = []) => {
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const [result, setResult] = useState(null)
useEffect(_ => {
Promise.resolve(runAsync(...deps))
.then(setResult, setError)
.finally(_ => setLoading(false))
}, deps)
return { loading, error, result }
}
const fetchJson = (url = "") =>
_fetch(url).then(r => r.json())
const useJson = (url = "") =>
useAsync(fetchJson, [url])
const MyComponent = ({ url = "" }) => {
const { loading, error, result } =
useJson(url)
if (loading)
return <pre>loading...</pre>
if (error)
return <pre style={{color: "tomato"}}>error: {error.message}</pre>
return <pre>result: {JSON.stringify(result, null, 2)}</pre>
}
const MyApp = () =>
<main>
ex 1 (success):
<MyComponent url="https://httpbin.org/get?foo=bar" />
ex 2 (error):
<MyComponent url="https://httpbin.org/status/500" />
</main>
ReactDOM.render(<MyApp />, document.body)
pre {
background: ghostwhite;
padding: 1rem;
white-space: pre-wrap;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>