当我尝试使用useEffect钩子从API获取数据时,我的React代码有一些问题。
这是我的代码:
const App = () => {
const [ data, setData ] = useState([]);
const [ loading, setLoading ] = useState(false);
useEffect(() => {
const loadData = async () => {
try {
setLoading(true);
const response = await fetch(`htts://localhost/api/`);
const json = await response.json();
console.log(json);
setData(json);
setLoading(false);
} catch (error) {
console.log("error: " + error);
}
}
loadData();
}, []);
}
我遇到的问题,当我渲染html它给我的错误,我无法映射到一些data
,我从我的API设置。
我认为设置data
变量将使我能够在模板中吐出数据和各种对象关键字名称,如:
{
data.keyname.map((item, index) => {
return (
<option key={ index }
value={ item.displayName }
>
{ item.displayName }
</option>
);
})
}
我是不是忽略了什么?似乎我的数据没有加载,所以渲染代码不能循环通过任何数据,它需要为了渲染应用程序。
如果你试图映射任何东西来返回你的组件,它必须初始化为一个数组。它是否为空并不重要,但必须是一个数组。
所以我建议你初始化你的"数据"。这样的:
const [ data, setData ] = useState({anyKeyName:[]});
什么是data.keyname
?data
以数组开始:
const [ data, setData ] = useState([]);
数组没有keyname
属性。所以这将失败,并出现一个错误,说你不能在undefined
上调用.map
:
data.keyname.map((item, index) => {
如果你从服务器返回的是一个对象,它有一个数组属性叫做keyname
,初始化你的默认对象来匹配它:
const [ data, setData ] = useState({ keyname: [] });
基本上听起来像你的useEffect
逻辑是工作的(假设响应是你所期望的),但你只是有一个不匹配之间的初始状态和你期望的状态是在渲染。
添加Optional chaining(?)
是快速而可靠的解决方案。
{
data?.keyname?.map((item, index) => {
return (
<option key={ index }
value={ item.displayName }
>
{ item.displayName }
</option>
);
})
}
了解更多Optional_chaining.
将数据初始化为空数组而不处理其他获取状态会使你的应用程序面临UI错误和硬错误——如果你处理了所有的数据获取状态,这些都是可以避免的。
在获取数据时,传统上至少要处理4种状态-
- 未获取数据(数据为null)
- 抓取(加载)
- 取回错误(Error)
- 已获取数据(Data)
查看下面的示例,如果出现问题或没有返回数据,如何在不破坏应用程序UI的情况下处理所有4种状态。
const App = () => {
const [ data, setData ] = useState(null);
const [ error, setError ] = useState(false);
const [ loading, setLoading ] = useState(false);
useEffect(() => {
const loadData = async () => {
setLoading(true);
try {
const response = await fetch(`htts://localhost/api/`);
// response is not asynchronous - don't await.
setData(response.json());
} catch (error) {
console.error("error: ", error); // make sure you remove this on build - you can also use common instead of concatenation
setError(true)
} finally {
setLoading(false); // move loading state so it always exits the loading state on error or success - currently if yours has an error your app will be stuck in loading state.
}
}
loadData();
}, []);
if( loading ) return <>Loading component</>;
if( error ) return <>Error component </>;
// check if data was returned and verify it's not just an empty array
if (!data || !data.keyname.length) return <>No data found component </>;
return (
<>
{data.keyname.map(({ displayName, id }) => (
<option
key={ id } // should use something unique to this set of elements (not index) like item.id - if the id is auto incremented then prefix it with something unique for this component like `keynames-${id}`
value={ displayName }
>
{ displayName }
</option>
)}
</>
)
}