jeudi 8 septembre 2022

React - How use to async calls within High Order Components?

I want to adjust a demo provided by some tutorial about React Design Patterns, subject: Higher Order Component, and want to use an external data source from the url: https://jsonplaceholder.typicode.com/users/1 to display the data within my form.

There are two issues I have now when trying to do it:

  1. How can I use a parameter for the resp. userId? In the tutorial it was just hardcoded within the URL. If I can add a prop like <UserInfoFormImproved userId={1} />, how can I access it and put this into the path - if that's the adequate way to do so.
  2. I guess since it's an async call, my Form always displays the "Loading part". What's the best way to solve this issue to ultimately receive the data? I can clearly see response.data not being empty when I log it, but the State variables are when I log them inside of the useEffect Hook

This is what I got so far.

Any help, tips, additional sources to learn this would be highly appreciated.

This is my HOC which I just copied:

import React, { useState, useEffect } from "react";
import axios from "axios";

const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);

export const withEditableResource = (Component, resourcePath, resourceName) => {
  return (props) => {
    const [originalData, setOriginalData] = useState(null);
    const [editedData, setEditedData] = useState(null);

    useEffect(() => {
      (async () => {
        const response = await axios.get(resourcePath);
        setOriginalData(response.data);
        setEditedData(response.data);
      })();
    }, []);

    const onChange = (changes) => {
      setEditedData({ ...editedData, ...changes });
    };

    const onSave = async () => {
      const response = await axios.post(resourcePath, {
        [resourceName]: editedData,
      });
      setOriginalData(response.data);
      setEditedData(response.data);
    };

    const onReset = () => {
      setEditedData(originalData);
    };

    const resourceProps = {
      [resourceName]: editedData,
      [`onChange${capitalize(resourceName)}`]: onChange,
      [`onSave${capitalize(resourceName)}`]: onSave,
      [`onReset${capitalize(resourceName)}`]: onReset,
    };

    return <Component {...props} {...resourceProps} />;
  };
};

That's my form, I want to use - in the last lines you can find the hard-coded URL path, I want to swap for a parameter:

import { withEditableResource } from "./withEditableResource";

export const UserInfoFormImproved = withEditableResource(
  ({ user, onChangeUser, onSaveUser, onResetUser }) => {
    const { name, email, username } = user || {};    

    return user ? (
      <>
        <label>
          Name:
          <input
            value={name}
            onChange={(e) => onChangeUser({ name: e.target.value })}
          />
        </label>
        <label>
          Email:
          <input
            value={email}
            onChange={(e) => onChangeUser({ email: e.target.value })}
          />
        </label>
        <label>
          Username:
          <input
            value={username}
            onChange={(e) => onChangeUser({ username: e.target.value })}
          />
        </label>
        <button onClick={onResetUser}>Reset</button>
        <button onClick={onSaveUser}>Save Changes</button>
      </>
    ) : (
      <p>Loading...</p>
    );
  },
  `https://jsonplaceholder.typicode.com/users/3`,
  "User"
);

And that's the actual use of this two components within my App - I've added my idea on how to solve the parameter argument here:

import { UserInfoFormImproved } from "./HigherOrderComponents/UserInfoFormImproved";
    
function App() {      

  return (
   <UserInfoFormImproved userId={1} />
  );
}

export default App;

Aucun commentaire:

Enregistrer un commentaire