1

I recently started using React Query to manage my data fetching and caching, but I'm having trouble figuring out how to update displayed data without constantly sending mutations.

For example, let's say I have a query at the top of my component to get a User, and then I display all the users in simple cards. Normally, if I want to update the displayed user's firstname, I would bind a function like handleFieldChange(id_user, field, value) to the input. This function would simply update the state of the given user, and then if I hit the save button, it would send the updated user to the server.

Here's some code that illustrates this approach (code blocks are just exemples):

// react
import { useState, useEffect } from 'react';

// api
import { getUsers, updateUser } from '../api/users';

const UserCardGrid: React.FC = () => {
    const [users, setUsers] = useState([]);

    // handlers
    const handleGetUsers = async () => {
        const { data } = await getUsers();
        setUsers(data);
    };
    const handleFieldChange = (id_user: number, key: string, value: any) => {
        const newUsers = users.map((user: any) => {
            if (user.id_user === id_user) {
                return {
                    ...user,
                    [key]: value,
                };
            }
            return user;
        });
        setUsers(newUsers);
    };
    const handleUpdateUser = async (id_user: number) => {
        const user = users.find((user: any) => user.id_user === id_user);
        await updateUser(id_user, user);
    };

    // effects
    useEffect(() => {
        handleGetUsers();
    }, []);

    return (
        <div className='grid'>
            {users.map((user: any) => (
                <div key={user.id_user}>
                    <input
                        key={user.id_user}
                        type='text'
                        value={user.username}
                        onChange={(e) =>
                            handleFieldChange(user.id_user, 'username',e.target.value)
                        }
                    />

                    <button onClick={() => handleUpdateUser(user.id_user)}>Update</button>
                </div>
            ))}
        </div>
    );
};

export default UserCardGrid;

However, with React Query, I can't do that. If I want to update the displayed text, I need to update the cache or send a useMutation query every time I type something. I know I can use useMutation and optimistic updates, but it seems wrong to send a mutation every time the user types something (even if i use a debounce mecanism).

// api
import { useQuery, useMutation } from '@tanstack/react-query';
import { getUsers, updateUser } from '../api/users';

const UserCardGrid: React.FC = () => {
    const usersQ = useQuery(['users'], getUsers);
    const userM = useMutation(updateUser);

    // handlers
    const handleFieldChange = (id_user: number, key: string, value: any) => {
        // HOW COULD I IMPLEMENT THIS?
    };

    const handleUpdateUser = async (id_user: number) => {
        userM.mutate(id_user, usersQ.data.find((user: any) => user.id_user === id_user));
    };

    return (
        <div className='grid'>
            {usersQ.data?.map((user: any) => (
                <div key={user.id_user}>
                    <input
                        key={user.id_user}
                        type='text'
                        value={user.username}
                        onChange={(e) =>
                            handleFieldChange(user.id_user, 'username',e.target.value)
                        }
                    />
                    <button onClick={() => handleUpdateUser(user.id_user)}>Update</button>
                </div>
            ))}
        </div>
    );
};

export default UserCardGrid;

I just want to be able to update my user how I want, and update it when I hit the save button. I could save all the users in a state, but I like how React auto-fetches when we come back to the page, so storing it in a state would be wrong too.

Does anyone have any suggestions for how to handle this situation with React Query? Thank you in advance for your help!

AnonymZ
  • 78
  • 1
  • 11

1 Answers1

0

For example, let's say I have a query at the top of my component to get a User, and then I display all the users in simple cards. Normally, if I want to update the displayed user's firstname, I would bind a function like handleFieldChange(id_user, field, value) to the input. This function would simply update the state of the given user, and then if I hit the save button, it would send the updated user to the server.

This is done using state (or a form library if you want to do validations etc). Your main component would pull the users and render cards. Your handle field change would update the state (that you copied from your query results). That state should be stored in the card (or higher). Then when the user clicks to save the record you would execute the react-query mutation that updates the record on the server side. In the onSettled for the mutation hook you would use the queryClient to invalidate the queries that hold that record in cache. The queries will re-run and you'll see the updated name show up in the cards.

Chad S.
  • 6,252
  • 15
  • 25
  • Actually, that's what I'm using now, but it feels wrong to store query results in states. I feel like it defeats the purpose of React Query. However, for now, it works – AnonymZ Mar 01 '23 at 20:02
  • 1
    You have to store it if you want to change it. (i.e. there must be some place for the 'altered' state to exist before it gets sent to the api). – Chad S. Mar 01 '23 at 21:37
  • 2
    I happen to have a blogpost on this exact topic: https://tkdodo.eu/blog/react-query-and-forms – TkDodo Mar 04 '23 at 19:27