调用useState触发无效Hook调用错误:违反React Hook规则
解决React Hook调用错误:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. 问题重现
编写Library组件时,尝试在useEffect内的异步函数中调用GetLibraries获取数据,触发了上述Hook调用错误。
原Library组件代码
export default function Library() { const [libs, setLibs] = useState([{label: "hello", value: "Hello"}]) useEffect(() => { const FuncLibraries = async () => { const data = await GetLibraries() // Calling setLibs here, which was defined at the top using the useState() hook setLibs(data) } FuncLibraries() console.log(libs) }, []); return ( <View> <Stack.Screen options={ { headerStyle: styles.HeaderStyle, headerTitleStyle: styles.HeaderTextStyle, headerRight:(() => <Dropdown style={styles.libraryDropDownScroll} itemContainerStyle={styles.libraryDropDownItem} containerStyle={styles.libraryDropDownContainer} data={libs} labelField="label" valueField="value" placeholder='Main' onChange={item => {}} renderRightIcon={() => (<Ionicons name="arrow-down" size={20} color={Colors.black} style={styles.closeIconStyle}/>)} /> ) } }/> </View> ); };
原GetLibraries函数代码
export default async function GetLibraries () { const [libs, setLibs] = useState([]); const email = await getItemFor("email"); useEffect(async () => { const { data, error } = await supabase .storage .from('pdfs') .list(`${email}/books`); if (data != null) { const libraries = []; for (let x = 0; x < data.length; x ++) { libraries.push({label: data[x].name, value: x.toString()}) }; setLibs(libraries); }}, []) return(libs) };
错误原因
GetLibraries是普通异步函数,并非React函数组件或自定义Hook,但在里面直接使用了useState和useEffect钩子,违反了React Hook的核心规则:
- Hooks只能在函数组件的顶层作用域调用
- 或者在自定义Hook(命名以
use开头)的顶层作用域调用
修正方案
把GetLibraries改成纯异步数据获取函数,移除所有Hook,直接处理并返回数据:
修正后的GetLibraries函数
export default async function GetLibraries () { const email = await getItemFor("email"); const { data, error } = await supabase .storage .from('pdfs') .list(`${email}/books`); if (data != null) { const libraries = []; for (let x = 0; x < data.length; x ++) { libraries.push({label: data[x].name, value: x.toString()}) }; return libraries; } return []; // 数据为空或出错时返回默认空数组 };
修正后的Library组件
export default function Library() { const [libs, setLibs] = useState([{label: "hello", value: "Hello"}]) useEffect(() => { const fetchLibraries = async () => { const data = await GetLibraries(); setLibs(data); console.log(data); // 直接打印返回的data,原console.log(libs)会拿到更新前的旧值 } fetchLibraries(); }, []); return ( <View> <Stack.Screen options={{ headerStyle: styles.HeaderStyle, headerTitleStyle: styles.HeaderTextStyle, headerRight: () => ( <Dropdown style={styles.libraryDropDownScroll} itemContainerStyle={styles.libraryDropDownItem} containerStyle={styles.libraryDropDownContainer} data={libs} labelField="label" valueField="value" placeholder='Main' onChange={item => {}} renderRightIcon={() => (<Ionicons name="arrow-down" size={20} color={Colors.black} style={styles.closeIconStyle}/>)} /> ) }}/> </View> ); };
额外说明
setLibs是异步操作,原代码中fetchLibraries()后直接调用console.log(libs)会拿到更新前的旧值,建议打印返回的data或添加监听libs变化的useEffect查看更新后的值。- 如果需要封装带Hook的逻辑,要遵循自定义Hook规则:命名以
use开头,比如useGetLibraries,但本次场景仅需纯数据获取,无需使用Hook。
内容的提问来源于stack exchange,提问作者Anirudh Kuppili
相关产品推荐
相关产品推荐

