如何在React中创建可消费外部Context的类服务提供者
1 Context重渲染问题原因与修复
你写的Context封装逻辑功能是正常的,触发全量重渲染的核心原因是:每次ApiProvider组件重渲染时,你传给Provider的value都是全新生成的对象,内部的get/post/put等方法也都是每次重新定义的新函数,React检测到Context值变化就会触发所有消费该Context的组件重渲染。
修复方案是用useCallback稳定所有请求方法,再用useMemo稳定Context的value值,只要依赖的外部参数不变化,Context值就不会更新:
import { createContext, useContext, useCallback, useMemo } from 'react'; export const ApiContext = createContext(); const ApiProvider = ({ children }) => { const api = externalApiContext(); // 用useCallback稳定函数引用,依赖项为所有用到的外部变量 const get = useCallback((url, params) => api.get(urlWithParams(`${baseUrl}${url}${admkey}`, params)), [api, baseUrl, admkey]); const post = useCallback((url, body, params = {}, headers = {}) => api.post(urlWithParams(`${baseUrl}${url}${admkey}`, params), { headers: { ...headers, 'Content-Type': 'application/json' }, body: JSON.stringify(body), }), [api, baseUrl, admkey]); const put = useCallback((url, body, params = {}, headers = {}) => api.put(urlWithParams(`${baseUrl}${url}${admkey}`, params), { headers: { ...headers, 'Content-Type': 'application/json' }, body: JSON.stringify(body), }), [api, baseUrl, admkey]); const remove = useCallback((url, params = {}, headers = {}) => api.remove(urlWithParams(`${baseUrl}${url}${admkey}`, params), { headers: { ...headers, 'Content-Type': 'application/json' }, }), [api, baseUrl, admkey]); const fetch = useCallback((url) => api.get(`${url}`), [api]); // 用useMemo稳定Context的value引用 const contextValue = useMemo(() => ({ get, post, put, remove, fetch }), [get, post, put, remove, fetch]); return ( <ApiContext.Provider value={contextValue}> {children} </ApiContext.Provider> ); };
如果baseUrl、admkey、externalApiContext返回的实例都是全局不变的常量,你甚至可以把所有请求方法和Context值都提到组件外部声明,完全避免重渲染风险。
2 支持Context消费的通用服务类实现方案
React的Context只能在组件或自定义Hook中消费,你可以通过「依赖注入+自定义Hook」的方式实现服务类和Context的绑定,不需要在类内部直接调用Context API:
实现步骤
- 先定义通用服务类,构造函数接收Context中导出的api实例作为参数:
class ApiService { constructor(api) { this.api = api; } // 封装业务通用请求方法示例 getUser(userId) { return this.api.get(`/users/${userId}`); } createUser(data) { return this.api.post('/users', data); } }
- 封装自定义Hook消费ApiContext,返回服务类的稳定实例:
export const useApiService = () => { const api = useContext(ApiContext); // 稳定实例引用,避免重复生成 return useMemo(() => new ApiService(api), [api]); };
- 在业务组件中直接调用即可:
const UserProfile = () => { const apiService = useApiService(); useEffect(() => { apiService.getUser(123).then(res => console.log(res)); }, [apiService]); return <div>用户信息</div> }
这种方案既保留了服务类的封装性,也完美适配React的Context规则,同时因为实例用useMemo稳定,不会触发额外重渲染。
内容的提问来源于stack exchange,提问作者Sonhja
相关产品推荐
相关产品推荐

