In an app with react, redux, and redux saga, where is the best place to put code which initiates a fetch of data, especially in a component which includes paging (and where that data for the current page is cached in the store and should only be fetched if it's not already cached)?
I can see from this question that one suggestion is to do this in the component itself (e.g. in componentDidMount), which is what I've done (except I've used a functional component using the useEffect hook instead), but the logic is now spread over a couple of places and doesn't seem clean.
Say a user clicks some link which takes them somewhere (e.g. using react-router) which loads a component showing a list of users. When that happens we want to initiate a fetch of users, and show some progress (e.g. a spinner) till the data is available. We also want the same thing to happen when a user clicks the paging controls to fetch the next page of users, etc.
Here's what I currently have, which I don't think is quite right:
- Initial population...
- The Users(functional) component loads.
- This receives props for page(the index of the current page) andusers(an array of users to show: initially null).
- It uses the useEffecthook to execute the side-effect of initiating a fetch. i.e. it dispatches aUSERS_FETCH_REQUESTEDaction.
- This is handled by a saga, which will eventually respond with USERS_FETCH_SUCCESSorUSERS_FETCH_ERROR.
- This will lead to the component being re-rendered, this time with the usersarray populated.
 
- The 
- Paging...
- The Userscomponent renders paging controls (buttons to go to the next page, last page, etc).
- Those buttons call a setPagefunction passed into props, to handle the page change.
- This dispatches a SET_PAGEaction to the store.
- The reducer then changes the pagevalue in the application state.
- This causes a re-render of the Userscomponent.
- When re-rendered, because this uses the useEffecthook, and this haspageas a dependency, this will kick off the fetch request as before.
 
- The 
The above all works, but maybe seems a bit messy because a UI component is responsible for causing a data fetch to occur. Also the fact that calling the setPage action has nothing in it to initiate a user fetch, when logically maybe it should. But if we do put that logic in that action, how do we then get the initial load to occur, when this isn't caused by a page change.
An important point to note is that we don't always want to fetch the data when the component is loaded: we only want to do that if the application's state in the store doesn't already contain the users for the current page. e.g. if page 1 of users is loaded, then the user navigates off to elsewhere in the app, then comes back to this component: the component should then just show the users already cached in the store.
Below is roughly what the code might look like with the current approach:
const Users: React.FC<{
    page: number,
    users: User[] | null,
    isFetching: boolean,
    fetchError?: Error,
    setPage: (page: number) => void,
    fetchUsers: (page: number) => void
}> = ({page, users, isFetching, fetchError, setPage, fetchUsers}): JSX.Element => {
    useEffect(() => {
        // Fetch users if we don't already have them in the props.
        !users && fetchUsers(page)
    }, [users, page, fetchUsers])
    return (
        <div>
            <button id="previousPageButton" onClick={() => setPage(page - 1)}><</button>
            <button id="nextPageButton" onClick={() => setPage(page + 1)}>></button>
            {fetchError
                ? <div>Error fetching users: {fetchError.message}</div>
                : (isFetching || !users
                    ? <div>Fetching...</div>
                    : users.map(user => <UserRow user={user} key={user.id}/>))
 
    