Home > Software engineering >  React: following DRY in React when fetching data from API
React: following DRY in React when fetching data from API

Time:11-03

When I am fetching the data from API, If I get Ok I will render the component to display the data, If an error has occured, I want to display a custom message for different components, and during fetching the data I want to show a loading message.

 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>
    </>
  );
}

I am planning to have a fetch call in multiple components. So, I was thinking If I could write the above logic in one place, like an HOC or Rendered Props and reuse it.

What I tried: but failed HOC wont work because you cant call hooks in normal functions. It has to be a Component. Rendered Props wont work as it gives this error: Error: Rendered more hooks than during the previous render.

Below is my failed rendered props code

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;

Loading and error logic is moved to the LoadingAndError component

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 });
};

CodePudding user response:

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

This line is calling props.render as a function, but you've passed in a component. Calling component directly can cause exactly the error you're seeing. If you want props.render to be used like this, then you should pass in a function which creates an element, like this:

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

Alternatively, if you want to keep passing in render={ShowData}, then change loadingAndError to create an element:

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}/>
}
  • Related