Date: Monday, March 20, 2023
Coming back to React.js after some time away, I got stuck and very frustrated when integrating these libraries together with React v18.x: "@tanstack/react-query", "react-hook-form", "react-select", and "@chakra-ui/react"...
I provided a trimmed down excerpt of package.json only to capture the lib versions used during this experience.
And of course, as is common in the javascript/front-end dev community, these libraries which I had never used before have become highly-recommended, must-use libraries, so I gave them a whirl. Surprisingly, these libs were quite pleasant to use coming from previous React experience (expect for react-select).
Note: I opted to use "chakra-react-select" so I didn't have to manually match react-select's <Select />
with the styles of chakra-ui's <Select />
.
Demo: CodeSandbox link
Most of the problems I ran into were stupid, non-obvious setup or library gotchas.
-
React-Query scoping: the component containing
<QueryClientProvider />
(e.g.<App />
) cannot also use react-query hooks... In my initial attempts, the currently shown<MyForm />
component was fully contained inside my<App />
definition, and I was getting this React-Query error:"Error: No QueryClient set, use QueryClientProvider to set one"
When googling the error, the fix was not obvious since I already had
<QueryClientProvider client={queryClient}>
in my<App />
A coworker informed me that "react-query / react-select logic needs to be down the component tree from the QueryClientProvider"... Ok, fair enough. 🤷 -
Getting React-Select to return a single value: react-select's standard approach wants options as type
[]RSOption
whereRSOption
's def is an object with props "value" and "label". Further, the value returned when a user selects an item in the dropdown is also an object withRSOption
props. Customizing the type of the returned user-selected value proved to be a challenge. However, I found a way by abstracting out react-select's<Select />
component, wrapping it as<MyDropdown />
and passing the react-hook-form value to<MyDropdown />
as a prop. Further, thevalue
prop needed a mapping function, and theonChange
prop must call react-hook-form's Controller'sfield.onChange()
with the new value. The end result almost looks like a traditional react.js "controlled component", however naturally with react-select unusual style/quirks and limitations. -
React-Hook-Form
getValues
: one limitation of react-hook-form'sgetValues()
function is it reads the state once and will not update or trigger re-renders. This is for "performance" as per the docs... Anyway, while debugging my integration of the libs, this limitation misled me into thinking my setup was not working. Had I tried using theonSubmit
handler, I would've seen that react-hook-form was indeed getting state changes from react-select... but I did not do look atonSubmit
. I spent a significant amount of time digging into the integration-- why user selections in react-select were not updating the rendered choice viagetValues
... Reading the react-hook-form docs, I re-learned to usewatch
for rendered state values... Thus it was a non-issue. Sadly, I had learned aboutgetValues
vswatch
months ago but forgot. -
React-Select's documentation is not great. As a dev who likes to read a library's docs before heading to google/stackoverflow/youtube, I struggled to find react-select's documentation helpful when getting started. Details on the options, or how to achieve custom behavioirs, were lacking. I found it helpful to inspect other developer's gist's and stackoverflow answers for non-standard implementations, and work backwards with their overrides. Hunt and peck, not a great approach but it ultimately bore fruit.
-
React-Select's API: the interface for react-select is very custom to that library. I don't like the design, it has an Angular.js v1.x feel rather than a React.js feel. I butted heads with this library many times--too many to doucment. However, I'll leave this one: Since I am using async data to populate the dropdown via React-Query, you would think that react-select's
AsyncSelect
component would be the right candidate, right? ...well, you'd be wrong. It turns out, after hours trying to figure out whyAsyncSelect
and React-Query were not playing well, I came across this commentFor what its worth, i was struggling with this myself and found the best option is actually not use the Async feature of react select and use reactQuery to inject the options to the normal select
So conclusing: don't use
AsyncSelect
for dropdown options loaded via React-Query useQuery hooks.