如何在 React 中创建一个使用外部上下文的 class 服务提供者?

How to create a class service provider that consumes an external context in React?

我正在尝试创建一个服务 class,它为整个应用程序提供对外部 Rest api.

的公共访问

我面临的第一个问题是这个外部 api 需要作为上下文使用,所以我的第一个方法是创建一个自定义上下文来使用和配置所有其余调用。见代码:

export const ApiContext = createContext();

const ApiProvider = ({ children }) => {
    const api = externalApiContext();

    const get = (url, params) =>
        api.get(urlWithParams(`${baseUrl}${url}${admkey}`, params));

    const post = (url, body, params = {}, headers = {}) =>
        api.post(urlWithParams(`${baseUrl}${url}${admkey}`, params), {
            headers: { ...headers, 'Content-Type': 'application/json' },
            body: JSON.stringify(body),
        });

    const put = (url, body, params = {}, headers = {}) =>
        api.put(urlWithParams(`${baseUrl}${url}${admkey}`, params), {
            headers: { ...headers, 'Content-Type': 'application/json' },
            body: JSON.stringify(body),
        });

    const remove = (url, params = {}, headers = {}) =>
        api.remove(urlWithParams(`${baseUrl}${url}${admkey}`, params), {
            headers: { ...headers, 'Content-Type': 'application/json' },
        });

    const fetch = (url) => api.get(`${url}`);

    return (
        <ApiContext.Provider value={{ get, post, put, remove, fetch }}>
            {children}
        </ApiContext.Provider>
    );
};

这很好用。我可以围绕应用程序进行配置和调用。 问题是:每当我进行这些调用时,所有这些调用都会重新呈现,因为我的上下文似乎已经改变

所以我有两个疑问:

  1. 此代码是否正确地提供了一个通用位置来提供一个上下文,该上下文提供一个外部 Rest api 上下文?我遗漏了使上下文重新呈现的内容?

另一方面,我正在尝试创建一个服务 class,就像这里发布的那样:https://newbedev.com/having-services-in-react-application。但是,在服务内部class,无法使用上下文...所以...

  1. 有什么方法可以创建能够使用外部上下文 api 的公共服务 Class?

提前致谢!

首先,您需要了解组件重新渲染的原因:

ApiProvider 重新呈现,因为它的父组件重新呈现或因为 children 已更改。 children 是这里最明显的候选人。

接下来的问题是每次 ApiProvider 渲染时,您都会提供一个全新的上下文值:

<ApiContext.Provider value={{ get, post, put, remove, fetch }}>
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                  \ new object everytime!

这迫使该上下文值的每个消费者也重新呈现。最简单的解决方案是用 useCallback 包装所有内容,用 useMemo.

包装上下文值本身

类似于:

export const ApiContext = createContext();

const ApiProvider = ({ children }) => {
  const api = externalApiContext();
  
  const get = useCallback(
    (url, params) => api.get(urlWithParams(`${baseUrl}${url}${admkey}`, params)),
    [api]
  );
  
  // ...repeat for a others...
  
  return (
    <ApiContext.Provider value={useMemo(
      () => ({ get, post, put, remove, fetch }),
      [get, post, put, remove, fetch]
    )} children={children}/>
  );
};