reactjs - ¿Cómo actualizar los datos mostrados con React Query sin enviar constantemente mutaciones?
Recientemente comencé a usar React Query para administrar la obtención y almacenamiento en caché de mis datos, pero tengo problemas para averiguar cómo actualizar los datos mostrados sin enviar constantemente mutaciones.
Por ejemplo, digamos que tengo una consulta en la parte superior de mi componente para obtener un Usuario, y luego muestro a todos los usuarios en tarjetas simples. Normalmente, si quiero actualizar el nombre del usuario mostrado, enlazaría una función como handleFieldChange(id_user, field, value) al campo de entrada. Esta función simplemente actualizaría el estado del usuario dado y luego, si presiono el botón de guardar, enviaría el usuario actualizado al servidor.
Aquí hay un código que ilustra este enfoque (los bloques de código son solo ejemplos):
// 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;
Sin embargo, con React Query, no puedo hacer eso. Si quiero actualizar el texto mostrado, necesito actualizar la caché o enviar una consulta useMutation cada vez que escribo algo. Sé que puedo usar useMutation y actualizaciones optimistas, pero parece incorrecto enviar una mutación cada vez que el usuario escribe algo (incluso si uso un mecanismo de debounce).
// 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;
Solo quiero poder actualizar mi usuario como quiera y actualizarlo cuando presione el botón de guardar. Podría guardar todos los usuarios en un estado, pero me gusta cómo React recupera automáticamente los datos cuando volvemos a la página, así que almacenarlo en un estado también sería incorrecto.
¿Alguien tiene alguna sugerencia sobre cómo manejar esta situación con React Query? ¡Gracias de antemano por su ayuda!
Respuesta #1
Puedes usar queryClient.setQueryData para actualizar los datos de la consulta como se menciona en los documentos.
En tu caso, handleFieldChange sería algo así
const queryClient = useQueryClient();
const handleFieldChange = (id_user: number, key: string, value: any) => {
queryClient.setQueryData(["users"], (users: any) =>
users.map((user: any) => {
if (user.id_user === id_user) {
return {
...user,
[key]: value,
};
}
return user;
}),
);
};
Respuesta #2
Por ejemplo, digamos que tengo una consulta en la parte superior de mi componente para obtener un Usuario, y luego muestro a todos los usuarios en tarjetas simples. Normalmente, si quiero actualizar el nombre del usuario mostrado, enlazaría una función como handleFieldChange(id_usuario, campo, valor) al input. Esta función simplemente actualizaría el estado del usuario dado, y luego, si presiono el botón de guardar, enviaría el usuario actualizado al servidor.
Esto se hace usando estado (o una biblioteca de formularios si quieres hacer validaciones, etc.). Tu componente principal obtendría los usuarios y renderizaría tarjetas. Tu función handle field change actualizaría el estado (que copiaste de los resultados de tu consulta). Ese estado debería almacenarse en la tarjeta (o en un nivel superior). Luego, cuando el usuario haga clic para guardar el registro, ejecutarías la mutación de react-query que actualiza el registro en el servidor. En el onSettled del hook de mutación, usarías el queryClient para invalidar las consultas que contienen ese registro en caché. Las consultas se volverán a ejecutar y verás que el nombre actualizado aparece en las tarjetas.
