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