I have a very simple react application using React Query with two react components: App and BotConfig. I am experiencing React Query functions within a component change their behavior from working as expected to not working after updating a useState
variable.
App --
Contains ReactQuery setup and maintains a single useState
React variable called currentBotId
, which is passed into BotConfig. currentBotId
is client-side state.
...
function App() {
const [currentBotId, setCurrentBotId ] = useState("Bot#0")
const bots = [...]
const queryClient = new QueryClient()
return (
<QueryClientProvider client={queryClient}>
<div id="app">
<BotConfig bots={botsList} setCurrentBotId={setCurrentBotId} currentBotId={currentBotId}/>
</div>
</QueryClientProvider>
)
export default App
BotConfig -- Updates currentBotId
and also performs create/delete actions on a Bot entities managed by React Query. bots
and their content are server-side state.
type Props = {
bots: Bot[]
currentBotId: string,
setCurrentBotId: (botId: string) => void
}
function BotConfig({bots, currentBotId, setCurrentBotId}: Props) {
const queryClient = useQueryClient()
// Create a new bot for the current bot id
const mutateCreateBot = useMutation({
mutationFn: (botId: string) => {
const bot = createBot(botId)
return Promise.resolve(bot)
},
onSuccess: (data) => {
queryClient.setQueryData(['getNewBot'], data)
},
})
// Delete the current bot
const mutateDeleteCurrentBot = useMutation({
mutationFn: (botId: string) => {
deleteBot(botId)
return Promise.resolve(botId)
},
onSuccess: (data) => {
queryClient.invalidateQueries({queryKey: ['getNewBot']})
},
})
const getNewBotQuery = useQuery({
queryKey: ['getNewBot'],
queryFn: () => {
const bot = getBot(currentBotId)
if (bot == undefined) { return null } else { return bot }
}
})
const handleClick = (botId: string) => {
setCurrentBotId(botId)
};
return (
<div>
<div>
<div>{getNewBotQuery.data != null ? <h1>{getNewBotQuery.data.id}</h1> : "No Bot Created"}</div>
<button
onClick={() => {mutateCreateBot.mutate(currentBotId)}}>
Create Bot
</button>
<button
onClick={() => {mutateDeleteCurrentBot.mutate(currentBotId)}}>
Delete Bot
</button>
</div>
<List>
{bots.map((bot) => (
<ListItem disablePadding>
<ListItemButton
selected={currentBotId == bot.id}
onClick={() => handleClick(bot.id)}
style={{ backgroundColor: bot.id == currentBotId ? 'lightblue' : 'green', }}>
<ListItemText primary={bot.id} />
</ListItemButton>
</ListItem>
))}
</List>
</div>
);
}
export default BotConfig;
This generates a UI with 3 bots that can either have content populated or not.
The problem I am facing is straight forward.
If I load the page and do not modify currentBotId
, everything works as expected. The create and delete buttons both modify the server state AND cause the client UI to update after the server updates.
If I modify currentBotId
by selecting another bot identifier, both buttons continue to update the server state as expected for the appropriate Bot, but no longer cause UI updates, even if I navigate back to the first bot that was originally working.
I have found that moving the useState
variable currentBotId
inside of the BotConfig component resolves the issues, so the root cause appears to be related to the fact that it is outside the component. In my full application moving currentBotId
into this component is not possible.
I am new to React and React Query, so any insight into why this would be failing is much appreciated.
Every time you use setCurrentBotId
it causes a rerender for App
component and it recreates a new instance of query client. To fix this, define the query client like below
const [queryClient] = useState(new QueryClient())