React Native:如何根据登录状态正确切换常量文件中的BaseUrl
React Native动态切换BaseUrl解决Invalid hook call错误
问题背景
我的React Native项目登录页面仅支持美国(+1)和印度(+91)区号的手机号,已在Login.js中根据登录手机号的区号,通过Redux分发setUS/setNonUS动作标记区域状态。尝试在constants.js中用useSelector获取Redux的isUS状态动态切换BaseUrl时,触发了「Invalid hook call」错误。此前单一BaseUrl声明时运行正常,需要修复该问题,同时保证其他组件仍能通过import {BaseUrl} from '../../utility/constants'的方式正常导入BaseUrl及相关EndPoint。
相关代码示例
Login.js
let BaseUrl; if (userLoginModel.code === '+91') { dispatch(setNonUS()); } else if (userLoginModel.code === '+1') { dispatch(setUS()); }
constants.js(错误写法)
// constants.js import {useSelector} from 'react-redux'; let BaseUrl; const isUS = useSelector(state => state.settings.isUS); if (isUS) { BaseUrl = 'https://us.herokuapp.com/api/v1'; } else { BaseUrl = 'https://india.herokuapp.com/api/v1'; } export {BaseUrl}; const customersHub = BaseUrl + '/customers'; const salesHub = BaseUrl + '/sales'; export const EndPoint = { login: customersHub + '/addCustomer', getSales: salesHub + '/getSalesOfCustomer', getCustomer: customersHub + '/getCustomer', }
错误原因
useSelector是React Hook,只能在React组件的顶层或自定义Hook的顶层调用,不能在普通JS工具模块(如constants.js)中直接使用,违反了Hook的使用规则,因此抛出Invalid hook call错误。
解决方案
方案一:用自定义Hook封装动态常量
这种方式既遵守Hook规则,又能让组件通过统一的方式获取最新的BaseUrl和EndPoint:
修改constants.js:
// constants.js // 定义基础域名常量 const US_BASE_URL = 'https://us.herokuapp.com/api/v1'; const IN_BASE_URL = 'https://india.herokuapp.com/api/v1'; // 自定义Hook,用于组件中获取当前的BaseUrl和EndPoint import { useSelector } from 'react-redux'; export const useAppConstants = () => { const isUS = useSelector(state => state.settings.isUS); const baseUrl = isUS ? US_BASE_URL : IN_BASE_URL; // 动态生成EndPoint const customersHub = baseUrl + '/customers'; const salesHub = baseUrl + '/sales'; return { BaseUrl: baseUrl, EndPoint: { login: customersHub + '/addCustomer', getSales: salesHub + '/getSalesOfCustomer', getCustomer: customersHub + '/getCustomer', }, }; }; // 兼容非组件环境(如API工具类)的同步获取方法 import store from '../path/to/your/store'; // 导入你的Redux store实例 export const getBaseUrl = () => { const isUS = store.getState().settings.isUS; return isUS ? US_BASE_URL : IN_BASE_URL; }; export const getEndPoints = () => { const baseUrl = getBaseUrl(); const customersHub = baseUrl + '/customers'; const salesHub = baseUrl + '/sales'; return { login: customersHub + '/addCustomer', getSales: salesHub + '/getSalesOfCustomer', getCustomer: customersHub + '/getCustomer', }; };
在组件中使用:
// 业务组件示例 import { useAppConstants } from '../../utility/constants'; const SalesComponent = () => { const { BaseUrl, EndPoint } = useAppConstants(); const fetchSalesData = async () => { try { const response = await fetch(EndPoint.getSales); const data = await response.json(); // 处理数据 } catch (err) { console.error(err); } }; return ( // 组件UI ); };
方案二:在Redux中直接维护BaseUrl状态
在登录时直接将BaseUrl存入Redux状态,后续从store中获取:
- 更新Redux reducer,添加baseUrl字段:
// settingsReducer.js const initialState = { isUS: false, baseUrl: 'https://india.herokuapp.com/api/v1', // 默认印度域名 }; const settingsReducer = (state = initialState, action) => { switch(action.type) { case 'SET_US': return { ...state, isUS: true, baseUrl: 'https://us.herokuapp.com/api/v1' }; case 'SET_NON_US': return { ...state, isUS: false, baseUrl: 'https://india.herokuapp.com/api/v1' }; default: return state; } }; export default settingsReducer;
- 修改Login.js,无需额外分发动作(reducer中已同步更新baseUrl):
// Login.js if (userLoginModel.code === '+91') { dispatch(setNonUS()); } else if (userLoginModel.code === '+1') { dispatch(setUS()); }
- 更新constants.js,用自定义Hook或同步方法获取:
// constants.js import { useSelector } from 'react-redux'; import store from '../path/to/your/store'; // 组件中使用的Hook export const useAppConstants = () => { const baseUrl = useSelector(state => state.settings.baseUrl); const customersHub = baseUrl + '/customers'; const salesHub = baseUrl + '/sales'; return { BaseUrl: baseUrl, EndPoint: { login: customersHub + '/addCustomer', getSales: salesHub + '/getSalesOfCustomer', getCustomer: customersHub + '/getCustomer', }, }; }; // 非组件环境使用的同步方法 export const getBaseUrl = () => store.getState().settings.baseUrl; export const getEndPoints = () => { const baseUrl = getBaseUrl(); const customersHub = baseUrl + '/customers'; const salesHub = baseUrl + '/sales'; return { login: customersHub + '/addCustomer', getSales: salesHub + '/getSalesOfCustomer', getCustomer: customersHub + '/getCustomer', }; };
注意事项
- 严格遵守React Hook使用规则:只能在组件或自定义Hook的顶层调用Hook,不能在普通JS模块、循环、条件判断中使用。
- 若需在非组件环境(如API请求工具类)中使用BaseUrl,直接从Redux store实例中同步获取状态,避免使用Hook。
- 动态生成EndPoint时,确保每次获取都是基于最新的BaseUrl,避免缓存旧值导致请求错误。
内容的提问来源于stack exchange,提问作者Piyush Chatterjee
相关产品推荐
相关产品推荐

