React:从 API 获取数据时在 React 中遵循 DRY

React: following DRY in React when fetching data from API

当我从 API 获取数据时,如果我得到 Ok,我将渲染组件以显示数据, 如果发生错误,我想为不同的组件显示自定义消息,并且在获取数据期间我想显示加载消息。

 export default function App() {
  const { data: products, loading, error } = useFetch(
    "products?category=shoes"
  );

  

  if (error) return <div>Failed to load</div>;
  if (loading) return <div>wait is Loading</div>;

  return (
    <>
      <div className="content">
          <section id="products">{products.map((product) =>{return <div>product.name</div>})}</section>
      </div>
    </>
  );
}

我计划在多个组件中进行提取调用。所以,我在想如果我能把上面的逻辑写在一个地方,比如 HOC 或 Rendered Props 并重用它。

我尝试了什么:但失败了 HOC 不会工作,因为你不能在正常函数中调用钩子。它必须是一个组件。 渲染的道具不会工作,因为它给出了这个错误: 错误:渲染的挂钩比上次渲染时多。

下面是我渲染失败的道具代码

const ShowData= function ({ data: products }) {
  
  return (
    <div>
  {products.map(product => {return <div>product.name</div>})}
  </div>
  );
};

function SearchCount({ count }) {
  return <h3>Search Results: {count} items found</h3>;
}

const Wrapper = function () {
  return (
    <LoadingAndError
      render={ShowData}
      params={{ url: "products?category=shoes" }}
    ></LoadingAndError>
  );
};

export default Wrapper;

加载和错误逻辑已移至 LoadingAndError 组件

const LoadingAndError = function (props) {

  const { url } = props.params;
  const { data: products, loading,error} = useFetch(url);
  if (loading)
    return <h1>Loading</h1>;
  if (error) return <h1>Error</h1>;

  return props.render({ data: products });
};

return props.render({ data: products });

此行将 props.render 作为函数调用,但您传入了一个组件。直接调用组件可能会导致您看到的错误。如果你想像这样使用 props.render,那么你应该传入一个创建元素的函数,如下所示:

<LoadingAndError
  render={(props) => <ShowData {...props} />}
  params={{ url: "products?category=shoes" }}
></LoadingAndError>

或者,如果你想一直传入render={ShowData},那么改变loadingAndError来创建一个元素:

const loadingAndError = function (props) {
  const { url } = props.params;
  const { data: products, loading,error} = useFetch(url);
  if (loading)
    return <h1>Loading</h1>;
  if (error) return <h1>Error</h1>;

  const Component = props.render
  return <Component data={products}/>
}