如何将useState中的餐厅sections数据存入另一个useState用于过滤?
解决方案:将Sanity返回的餐厅分区数据存入useState并实现过滤功能
核心问题分析
你当前直接用let restaurantSections = restaurantData.sections存在两个关键问题:
- 初始渲染时
restaurantData是空数组,restaurantData.sections会返回undefined,触发map渲染报错 - 当
restaurantData异步更新后,restaurantSections不会自动同步,可能导致过滤逻辑使用旧数据
具体实现步骤
1. 新增分区数据的专属useState
添加专门存储餐厅分区的状态变量,初始值设为空数组:
const [restaurantSections, setRestaurantSections] = useState([]);
2. 同步更新分区数据
在获取到Sanity返回的餐厅数据后,将其中的sections同步到新状态中,同时做容错处理避免报错:
const { slug } = useParams(); const [restaurantData, setRestaurantData] = useState([]); const [sectionsVisibility, setSectionsVisibility] = useState(true); const [restaurantSections, setRestaurantSections] = useState([]); const [dishesData, setDishesData] = useState([]); useEffect(() => { sanityClient .fetch( ` *[slug.current == "${slug}"] { ..., sections[] -> { ..., dishes[] -> } }` ) .then((data) => { setRestaurantData(data[0]); // 用可选链+默认值避免data[0]为空时的报错 setRestaurantSections(data[0]?.sections || []); }); }, [slug]);
3. 调整过滤函数逻辑
过滤时直接使用restaurantSections状态变量,确保拿到最新数据,同时修正逻辑为返回分区下的餐品(而非整个分区对象):
const filter = (name) => { const targetSection = restaurantSections.find((section) => section.name === name); // 容错处理,避免找不到对应分区时返回undefined setDishesData(targetSection?.dishes || []); };
4. 优化列表渲染的容错性
渲染分区列表前先判断数据是否存在,避免初始加载时的报错:
{restaurantSections.length > 0 ? ( restaurantSections.map((section) => ( <li key={section._id} onClick={() => { // setSectionsVisibility(false); filter(section.name); console.log(section.name) }} > <div> {section.image ? ( <img src={urlFor(section.image).url()} alt="food" /> ) : ( <img src={foodImg} alt="food" /> )} <h1>{section.name}</h1> <span> <HiArrowSmRight size={20} className="arrowIcon" /> </span> </div> </li> )) ) : ( <li>加载中...</li> )} </ul>
注:你原代码中restaurantData.image应为section.image,每个分区的图片属于自身而非餐厅整体。
更优实现思路
不需要单独维护restaurantSections状态,直接基于restaurantData结合useMemo优化性能,减少状态变量的冗余:
const { slug } = useParams(); const [restaurantData, setRestaurantData] = useState(null); const [selectedSectionName, setSelectedSectionName] = useState(null); const [sectionsVisibility, setSectionsVisibility] = useState(true); useEffect(() => { sanityClient .fetch( ` *[slug.current == "${slug}"] { ..., sections[] -> { ..., dishes[] -> } }` ) .then((data) => setRestaurantData(data[0])); }, [slug]); // 用useMemo缓存过滤结果,避免每次渲染重复计算 const dishesData = useMemo(() => { if (!restaurantData?.sections) return []; if (!selectedSectionName) return []; // 可根据需求改为返回所有餐品 const targetSection = restaurantData.sections.find(section => section.name === selectedSectionName); return targetSection?.dishes || []; }, [restaurantData, selectedSectionName]); // 点击事件直接设置选中的分区名称 const handleSectionClick = (name) => { // setSectionsVisibility(false); setSelectedSectionName(name); };
这种方式减少了状态变量的维护成本,利用useMemo避免不必要的计算,逻辑更简洁高效。
内容的提问来源于stack exchange,提问作者Abdellah Slimani
相关产品推荐
相关产品推荐

