I've seen this question asked elsewhere but none of the answers helped me. I'm using a parameter in the url and I'm wanting to access it with the useParam hook to then query my backend.
The destructured param is typed as 'string | undefined' - Is there a way to definitely type the destructured query param? I'd like to avoid having undefined values. I'm aware that I can just handle the undefined value in the functions that are expecting the param but I would just rather remove the issue at source, if possible.
Other answers suggested matching the destructured param value to the param name stipulated in the route component, but mine already match. Is it perhaps something to do with the nested routes under '/journalEntries', i.e. because other routes -without params- are possible on the 'journalEntries' route? I tried to figure this out by deleting the other child routes but it didn't help.
Routing:
const Routing = () => {
return (
<MainViewContainer>
<NavBar />
<>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/thoughtRecords" element={<ThoughtRecordPage />} />
<Route path="/journalEntries">
<Route
index
element={
<JournalPage>
<JournalContainer />
</JournalPage>
}
/>
=========================================================
ROUTE I WANT TO DESTRUCTURE THE 'id' PARAMETER FROM:
<Route
path=":id"
element={
<JournalPage>
<JournalEntry />
</JournalPage>
}
/>
=========================================================
<Route
path="create"
element={
<JournalPage>
<JournalEntryForm />
</JournalPage>
}
/>
</Route>
</Routes>
</>
</MainViewContainer>
);
};
export default Routing;
Component containing link to single 'Journal Entry':
const JournalContainer = () => {
const { journalEntries } = useDataContext();
return (
<>
<div className="flex flex-col items-center">
{journalEntries.map((journalEntry) => {
return (
<div
className="w-1/3 border flex justify-between p-1 m-1"
key={journalEntry._id}
>
======================================================================
LINK TO JOURNAL ENTRY
<Link
to={`/journalEntries/${journalEntry._id}`}
className="w-3/4 flex justify-between"
>
======================================================================
<span className="">{journalEntry.title}</span>
<span className="text-xs">
{new Date(journalEntry.createdAt).toDateString()}
</span>
</Link>
<button
onClick={async () => await deleteJournalEntry(journalEntry._id)}
className="mr-0"
>
Delete
</button>
</div>
);
})}
<Link
className="w-1/3 border text-center p-1 m-1"
to={'/journalEntries/create'}
>
Create a Journal Entry
</Link>
</div>
</>
);
};
export default JournalContainer;
'Journal Entry' component which relies on the destructured parameter:
const { id } = useParams(); // is typed as 'string | undefined'
const methods = useForm({
defaultValues: async () => await getJournalEntryById(id), // this function expects a string
});
const navigate = useNavigate();
const [saveCopy, setSaveCopy] = useState(false);
const [readOnly, setReadOnly] = useState(true);
const submissionHandler: SubmitHandler<CreateJournalEntryType> = async (
data
) => {
await updateJournalEntryById(id, data); // this function expects a string
if (saveCopy) {
writeJournalEntryToFile(data);
}
navigate('/journalEntries');
};
return (
<FormProvider {...methods}>
<form
className="flex flex-col justify-evenly items-center border p-10 h-1/2"
onSubmit={methods.handleSubmit(submissionHandler)}
>
<button className="mr-auto" onClick={() => navigate(-1)}>
Go Back
</button>
<JournalEntryFormFields readOnly={readOnly} setReadOnly={setReadOnly} />
<div className="flex w-full justify-evenly items-center">
<button type="submit" className="border p-2 m-1">
Submit Journal Entry
</button>
<div className="flex items-center border p-1">
<input
type="checkbox"
className="m-1"
onChange={() => setSaveCopy(!saveCopy)}
/>
<label className="m-1" htmlFor="save-journal-entry-checkbox">
Save a copy to file
</label>
</div>
</div>
</form>
</FormProvider>
);
};
export default JournalEntry;
If you know for a fact that the id
route path param will always be a defined string then you can simply type/cast the param value. Something as simple as the follow should/could work.
const { id } = useParams() as { id: string };
If there's not this guarantee that id
will always be a route path parameter then you can provide a fallback value.
const { id = "" } = useParams();
or check that is is a valid value prior to passing it along.
const submissionHandler: SubmitHandler<CreateJournalEntryType> = async (
data
) => {
await updateJournalEntryById(id ?? "", data);
if (saveCopy) {
writeJournalEntryToFile(data);
}
navigate('/journalEntries');
};
or
const submissionHandler: SubmitHandler<CreateJournalEntryType> = async (
data
) => {
if (id) {
await updateJournalEntryById(id, data);
if (saveCopy) {
writeJournalEntryToFile(data);
}
navigate('/journalEntries');
}
};