As of React starts to evolve into direction of functional programming and useState, useEffect and custom hooks have been around for a while, I would suggest you write your API calls as custom hooks. This is great way of dealing with side effects and it gives you lot of power in sense of reusability. Not the least functions are way much more easier to test than classes. Here is simple demo https://codesandbox.io/s/practical-cohen-mh0ee
In essence you write separate functions in which you wrap your API calls, subsriptions or some calculation logic and then use them in your functional components. You can mix and match useEffect and useState -hooks into your custom hooks as you like. When you get hang of it and can start to effeciently write small functions this is ultimately extremely effecient way to handle complexity.
As of where you use these custom hooks there are no rules written into stone. Just stay consitent with your design and things will go smoothly. Personally I like to use custom hooks where data is actually needed, but might do some exceptions if end result is more practical or makes more sense.
for representational purposes code pasted here:
const useApiFetch = () => {
const [json, setJson] = React.useState();
React.useEffect(() => {
// throttle api response for visual purposes
setTimeout(async () => {
const response = await fetch(
"https://jsonplaceholder.typicode.com/todos/1"
);
const json = await response.json();
setJson(json);
}, 1000);
}, []);
return json;
};
const Component1 = () => {
const json = useApiFetch();
if (!json) return <p>loading...</p>;
return (
<p>
Component 1: <i>{JSON.stringify(json)}</i>
</p>
);
};
const Component2 = () => {
const json = useApiFetch();
if (!json) return <p>loading...</p>;
return (
<p>
Component 2: <i>{JSON.stringify(json)}</i>
</p>
);
};
export default function App() {
return (
<div className="App">
<h2>Component 1</h2>
<Component1 />
<h2>Component 2</h2>
<Component2 />
</div>
);
}