Search code examples
reactjsaxiosreact-component

trigger Axios data fetch on button click in another component


I have component structure like this

src
 --App
   --DataTableComponent
   --ButtonComponnet
axios
 --useAxios

On app load I am calling Axios library in DataTableComponent (through custom hook useAxios) to fetch some data, then later when user clicks a button in ButtonComponent I would like Axios to load data again in DataTableComponent, but I am not sure how as the components are siblings.


Solution

  • In your situation what you'll want to do is lift state up. Here a link to the official react documentation

    In addition, what I've done is created a code sandbox sample for your particular situation that demonstrates how you could do some thing similar in your app. Here's the link to that code sandbox https://codesandbox.io/s/romantic-brook-sfvpd?file=/src/App.tsx

    The principle behind lifting state up is that if you have 2 or more components that must share information then lift that information one level up to their parent. So, what I do, as you see in the code sandbox is that the app component now gets the information and pushes it down to your data table component.

    So, you'll want your useAxios hook to live in your parent app component. The button components job is to simply trigger the fetch.

    Once the fetch is triggered, new data is returned by the useAxios hook.

    The fetch also causes the App's useEffect hook to run and this updates the state and pushes the data to the data table component.

    So, in your case you'll probably want to wrap your useAxios hook in your own custom hook so you can pass parameters which in turn your useAxios hook can use to fetch data from your API.

    Continue to click on the fetch data button and each time I return a random number of items so you'll see your data components data getting updated. Remember to open the console to see the useAxios hook getting called followed by the data table contents being updated.

    I have used a similar approach in some of my production apps and in those apps I've created similar custom wrapper around useSWR hooks

    Using redux or some thing similar is a good idea if you have data that must be shared across the application i.e. global data but data that is specific to a few components doesn't need that approach and one should then go with the "lift state up" way of doing things.

    For completeness the code is also given below.

    import { useEffect, useState } from "react";
    import "./styles.css";
    
    // useAxios hook
    const useAxios = (_: boolean) => {
      console.log("useAxios");
    
      // mock data
      const arr = [
        {
          name: "Bianca Paul",
          phone: "1-453-676-9140",
          email: "[email protected]",
          address: "221-3571 Nam Street",
          id: 8
        },
        {
          name: "Hadley Gordon",
          phone: "1-235-486-3229",
          email: "[email protected]",
          address: "3255 Nec, Road",
          id: 3
        },
        {
          name: "Irma Bryan",
          phone: "1-818-417-5465",
          email: "[email protected]",
          address: "136-222 Facilisis Rd.",
          id: 2
        },
        {
          name: "Simon Nash",
          phone: "1-872-216-6482",
          email: "[email protected]",
          address: "Ap #873-5860 Erat St.",
          id: 101
        },
        {
          name: "Ursula Fleming",
          phone: "(998) 407-7291",
          email: "[email protected]",
          address: "110-1550 Phasellus Ave",
          id: 43
        }
      ];
    
      // Randomize the data
      function getRandomItem() {
        // get random index value
        let randomIndex = Math.floor(Math.random() * arr.length);
        if (randomIndex === 0) randomIndex = 1;
        // get random items
        const item = arr.slice(0, randomIndex);
    
        return item;
      }
    
      // return a promise
      const data = new Promise<any>((resolve, reject) => {
        setTimeout(() => {
          return resolve(getRandomItem());
        }, 1000);
      });
    
      return { data };
    };
    
    // Button component
    const ButtonComponent = (props: { clickCallback: () => void }) => {
      return (
        <>
          <button onClick={props.clickCallback}>fetch data</button>
        </>
      );
    };
    
    // DataComponent
    const DataTableComponent = ({
      data
    }: {
      data:
        | [
            {
              name: string;
              phone: string;
              email: string;
              address: string;
              id: string;
            }
          ]
        | null;
    }) => {
      return (
        <>
          {data ? (
            data.map((v) => (
              <div key={v.id}>
                {v.name},{v.address}, {v.phone}
              </div>
            ))
          ) : (
            <span>loading</span>
          )}
        </>
      );
    };
    
    // App Parent component
    export default function App(): JSX.Element {
      const [fetch, setFetch] = useState(true);
      const [returnedData, setReturnedData] = useState<
        | [
            {
              name: string;
              phone: string;
              email: string;
              address: string;
              id: string;
            }
          ]
        | null
      >(null);
      const { data } = useAxios(fetch);
      const buttonClicked = () => {
        setFetch(true);
      };
    
      useEffect(() => {
        let isMounted = true;
        if (fetch) {
          (async () => {
            if (isMounted) setReturnedData(await data);
            if (isMounted) setFetch(false);
            console.log(await data);
          })();
        }
        return function () {
          isMounted = false;
        };
        // eslint-disable-next-line react-hooks/exhaustive-deps
      }, [fetch]);
    
      return (
        <div className="App">
          <h1>Lift state up</h1>
          <div>
            <DataTableComponent data={returnedData} />
          </div>
          <div>
            <ButtonComponent clickCallback={buttonClicked} />
          </div>
        </div>
      );
    }