的value属性,并试图通过e.target.value获取,通常会导致null或undefined,从而使API调用失败。解决方案一:使用
最直接且语义化的解决方案是使用
代码示例:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const API_BASE_URL = "https://www.themealdb.com/api/json/v1/1/filter.php?c="; // 示例API
function CategoryFilter() {
const [category, setCategory] = useState("Seafood"); // 初始分类
const [meals, setMeals] = useState([]);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (!category) return; // 如果没有分类,则不发起请求
setLoading(true);
setError(null);
axios
.get(`${API_BASE_URL}${category}`)
.then(function (response) {
setMeals(response.data.meals || []); // 确保即使没有数据也设置为空数组
})
.catch(function (err) {
console.error("API Error:", err);
setError("加载数据失败,请稍后再试。");
setMeals([]);
})
.finally(() => {
setLoading(false);
});
}, [category]); // category 改变时重新运行 effect
const onClickHandler = (e: React.MouseEvent<HTMLButtonElement>) => {
// 使用 e.currentTarget.value 获取 button 的值
setCategory(e.currentTarget.value);
};
return (
<div>
<h1>菜品分类</h1>
<ul style={{ listStyle: 'none', padding: 0 }}>
<li style={{ display: 'inline-block', margin: '0 10px' }}>
<button value="Seafood" onClick={onClickHandler}>
海鲜 (Seafood)
</button>
</li>
<li style={{ display: 'inline-block', margin: '0 10px' }}>
<button value="Vegetarian" onClick={onClickHandler}>
素食 (Vegetarian)
</button>
</li>
<li style={{ display: 'inline-block', margin: '0 10px' }}>
<button value="Dessert" onClick={onClickHandler}>
甜点 (Dessert)
</button>
</li>
</ul>
{loading && <p>加载中...</p>}
{error && <p style={{ color: 'red' }}>{error}</p>}
{!loading && !error && meals.length === 0 && <p>未找到相关菜品。</p>}
{!loading && !error && meals.length > 0 && (
<div>
<h2>{category} 菜品:</h2>
<ul>
{meals.map((meal: any) => (
<li key={meal.idMeal}>{meal.strMeal}</li>
))}
</ul>
</div>
)}
</div>
);
}
export default CategoryFilter;注意事项:
AI Surge Cloud
低代码数据分析平台,帮助企业快速交付深度数据
87
查看详情
- 在onClickHandler中,我们使用e.currentTarget.value来获取按钮的值。currentTarget指的是事件监听器所绑定的元素,而target可能是事件实际发生的元素(例如,如果按钮内有文本,target可能是文本节点)。对于按钮,两者通常相同,但currentTarget在处理事件委托时更可靠。
- 将button嵌套在li中是完全符合语义的,因为li代表列表项,而button是列表项中的一个可交互元素。
解决方案二:使用 data-* 属性(如果必须使用
元素本身作为可点击项,并且需要传递自定义数据,那么HTML5的data-*属性是理想的选择。data-*属性允许你在标准HTML元素上存储自定义数据,而不会影响其语义或行为。代码示例:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const API_BASE_URL = "https://www.themealdb.com/api/json/v1/1/filter.php?c="; // 示例API
function CategoryFilterWithDataAttribute() {
const [category, setCategory] = useState("Seafood"); // 初始分类
const [meals, setMeals] = useState([]);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (!category) return;
setLoading(true);
setError(null);
axios
.get(`${API_BASE_URL}${category}`)
.then(function (response) {
setMeals(response.data.meals || []);
})
.catch(function (err) {
console.error("API Error:", err);
setError("加载数据失败,请稍后再试。");
setMeals([]);
})
.finally(() => {
setLoading(false);
});
}, [category]);
const onClickHandler = (e: React.MouseEvent<HTMLLIElement>) => {
// 使用 e.currentTarget.getAttribute("data-value") 获取自定义数据
const clickedCategory = e.currentTarget.getAttribute("data-value");
if (clickedCategory) {
setCategory(clickedCategory);
}
};
return (
<div>
<h1>菜品分类 (使用 data-value)</h1>
<ul style={{ listStyle: 'none', padding: 0 }}>
<li data-value="Seafood" onClick={onClickHandler} style={{ cursor: 'pointer', display: 'inline-block', margin: '0 10px', padding: '5px 10px', border: '1px solid #ccc' }}>
海鲜 (Seafood)
</li>
<li data-value="Vegetarian" onClick={onClickHandler} style={{ cursor: 'pointer', display: 'inline-block', margin: '0 10px', padding: '5px 10px', border: '1px solid #ccc' }}>
素食 (Vegeta
rian)
</li>
<li data-value="Dessert" onClick={onClickHandler} style={{ cursor: 'pointer', display: 'inline-block', margin: '0 10px', padding: '5px 10px', border: '1px solid #ccc' }}>
甜点 (Dessert)
</li>
</ul>
{loading && <p>加载中...</p>}
{error && <p style={{ color: 'red' }}>{error}</p>}
{!loading && !error && meals.length === 0 && <p>未找到相关菜品。</p>}
{!loading && !error && meals.length > 0 && (
<div>
<h2>{category} 菜品:</h2>
<ul>
{meals.map((meal: any) => (
<li key={meal.idMeal}>{meal.strMeal}</li>
))}
</ul>
</div>
)}
</div>
);
}
export default CategoryFilterWithDataAttribute;注意事项:
- 在HTML中,data-*属性的命名规则是data-前缀后跟自定义名称(如data-value)。
- 在J*aScript中,你可以通过元素的dataset属性(e.currentTarget.dataset.value)或getAttribute()方法(e.currentTarget.getAttribute("data-value"))来访问这些数据。dataset属性提供了一个更方便的DOMStringMap接口,将data-value转换为dataset.value。
- 当
- 被用作可点击元素时,为了提高用户体验和可访问性,建议为其添加cursor: pointer样式,并考虑添加键盘导航支持。
总结
在React应用中实现动态API调用时,选择正确的HTML元素和属性至关重要。
-
首选: 当你需要一个可点击的交互元素来触发动作并传递值时,是语义上最正确的选择。它的value属性可以可靠地存储和传递数据。
- *`data-属性作为备选:** 如果你必须使用非表单元素(如
- 、等)作为可点击项,并且需要关联自定义数据,那么data-*`属性是最佳方案。它允许你在不违反HTML语义的前提下存储额外信息。
-
避免误用
- 的value属性:
记住
- 的value属性仅用于指定有序列表中的序数值,不应用于存储自定义字符串数据。
通过遵循这些最佳实践,你可以构建出更健壮、更易维护且用户体验更好的React应用。