React解构coinsData实现id全局使用及CoinProvider TS报错求助
问题修复方案
报错核心原因
你遇到的TS报错Type '({ children }: { children?: ReactNode; }) => void' is not assignable to type 'FC<{}>'由以下几个错误共同导致:
- 你将
CoinContext.Provider的返回逻辑写在了coinsData.map的遍历回调中,整个CoinProvider组件没有合法的顶层返回值,不符合FC类型要求必须返回JSX元素的规则 - 初始定义的
CoinContext类型为Coin[] | undefined,但实际传入Provider的value是包含分页方法、加载状态等字段的对象,类型完全不匹配 useEffect中写了setTotalPages(totalPages)的无用代码,会触发无限重渲染
同时你要全局使用id、保留完整coinsData的需求逻辑也存在错误:在map遍历中取到的是每个币种的id,无法直接全局使用,套在map里的Provider会重复渲染多次,逻辑完全不成立。
完整修复代码
import React,{FC, useState, useEffect, ReactNode, createContext, useContext} from 'react' export interface Coin { id:string; name: string; current_price: number; symbol:string; price_change_percentage_24h:number image:string; market_cap:number market_cap_rank:number } // 定义Context的准确类型,匹配实际要全局暴露的字段 interface CoinContextType { coinsData: Coin[]; totalPages: number; loading: boolean; handlePrevPage: () => void; handleNextPage: () => void; currentPage: number; // 全局存储选中的币种id,不需要可以删除 selectedId: string; setSelectedId: (id: string) => void; } export const CoinContext = createContext<CoinContextType | undefined>(undefined) // 封装自定义消费hook,避免每次使用都要判空 export const useCoinContext = () => { const context = useContext(CoinContext) if (!context) throw new Error('useCoinContext 必须在CoinProvider内部使用') return context } export const CoinProvider:FC<{children: ReactNode}> = ({children}) => { const [loading, setLoading] = useState(false) const [page, setPage] = useState(1); const [totalPages, setTotalPages] = useState(10); const [coinsData, setCoinsData] = useState<Coin[]>([]) // 全局选中id的状态,不需要可以删除 const [selectedId, setSelectedId] = useState<string>('') const handlePrevPage = () => { if (page > 1) setPage(prev => prev - 1); }; const handleNextPage = () => { if (page < totalPages) setPage(prev => prev + 1); }; useEffect(()=>{ const fetchData = async () => { setLoading(true); const response = await fetch(`https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&page=${page}&per_page=10&sparkline=false`) const result = await response.json() setCoinsData(result); setLoading(false) } fetchData() },[page]) // Provider只需要渲染一次,所有全局值直接传入 return ( <CoinContext.Provider value={{ coinsData, totalPages, loading, handlePrevPage, handleNextPage, currentPage: page, selectedId, setSelectedId }}> {children} </CoinContext.Provider> ) }
使用示例
在任意消费Context的组件中,可以直接满足你的需求:
const CoinList = () => { const { coinsData, selectedId, setSelectedId } = useCoinContext() return ( <div> {/* 遍历完整coinsData渲染列表 */} {coinsData.map(coin => ( <div key={coin.id} onClick={() => setSelectedId(coin.id)}> <img src={coin.image} alt={coin.name} width={20}/> <span>{coin.name}({coin.symbol.toUpperCase()})</span> <span>¥{coin.current_price}</span> </div> ))} {/* 全局使用选中的id */} {selectedId && <p>当前选中币种ID:{selectedId}</p>} {/* 直接提取所有id数组 */} <p>当前页所有币种ID:{coinsData.map(item => item.id).join('、')}</p> </div> ) }
内容的提问来源于stack exchange,提问作者mura1
相关产品推荐
相关产品推荐

