I am getting an error when I pass react-router props sent to a component in a Route because I have specific state that I pass this component but the error shows up in my Route.
Here is the Route code:
<Route
exact
path='/flipltimeline'
render={props => <FliplTimeline {...props} />
In another component I call this below
props.history.push(`/flipltimeline`, {
Approval: singleFliplApprovals,
InvestigationID,
Unit: unit,
Cost: cost
});
Here is the code for the Component. I finally got Typescript to compile this but I had to merge LocationState & TimelineState to get this to work. But now Typescript throws the above screenshot when I send props to my FliplTimeline component. Anyone have and idea how to fix this?
history.tsx
import { createBrowserHistory } from 'history';
let baseNameProd = '';
if (process.env.NODE_ENV !== 'production') {
console.log('Looks like we are in development mode!');
baseNameProd = '';
} else {
baseNameProd = '/flipl';
}
const customHistory = createBrowserHistory({
basename: baseNameProd
});
export default customHistory;
FliplTimeline.tsx
import * as React from 'react';
import { History, LocationState } from 'history';
interface FliplTimelineLocationState {
Approval: any;
InvestigationID: number;
Unit: string;
Cost: number;
}
interface TimelineState{
state: FliplTimelineLocationState;
}
interface Props {
history: History;
location: LocationState & TimelineState;
}
function FliplTimeline(props: Props) {
return (
<ModuleTemplate title='Status Timeline' subtitle=''>
<FliplTimelineJumbotron className='bg-primary-darker shadow-4 line-height-serif-4'>
<div className='grid-row'>
<div className='grid-col-4'>
<span
className='font-mono-2xl text-white'
style={{
verticalAlign: 'middle'
}}
>
FLIPL{' '}
</span>
<span
className='font-mono-xl text-gold'
style={{
verticalAlign: 'middle'
}}
>
{props.location.state.InvestigationID}
</span>
Update: Added my history.tsx file in which I created my own history for React-Router. Also added in the import statements.
Update: Tried to change my FliplTimeline component to have this interface
import { RouteComponentProps } from 'react-router-dom'
function FliplTimeline(props: RouteComponentProps ) {
I get 2 errors. First one is this one and another that says that the shape of the props are wrong. Ideas?
Update: I was able to finally get the right props declaration for my component.
import { RouteComponentProps } from 'react-router-dom';
interface FliplTimelineLocationState {
Approval: any;
InvestigationID: number;
Unit: string;
Cost: number;
}
function FliplTimeline(
props: RouteComponentProps<{}, any, FliplTimelineLocationState | any>
)
I was able to get it working.
import { RouteComponentProps } from 'react-router-dom';
interface FliplTimelineLocationState {
Approval: any;
InvestigationID: number;
Unit: string;
Cost: number;
}
function FliplTimeline(
props: RouteComponentProps<{}, any, FliplTimelineLocationState | any>
)
Use RouteComponentProps
and feed it the appropriate types for its generic. This article covers quite a bit: How to Use React Router in Typescript.
Using React Router with TypeScript almost necessitates filling in the generics that React Router has. Otherwise there isn't enough context to determine the types of everything. Compare to my example below. I use useHistory
and fill it with the type that I want to be made available. This would probably be your FliplTimelineLocationState
that way the state
property of history
would be determinable to be of type FliplTimelineLocationState
.
import React, {MouseEventHandler} from "react";
import {Route, RouteComponentProps, StaticContext, useHistory} from "react-router";
import {BrowserRouter, Link} from "react-router-dom";
interface IMyScreenRouteParams {
foo: string;
}
// You don't have to extend, you could just use StaticContext
interface IMyStaticContext extends StaticContext {
bar: string;
}
interface IHistory {
fizz: string;
}
const Nav = () => {
const history = useHistory<IHistory>();
const clickHanlder: MouseEventHandler = () => {
history.push("/my-screen", {
fizz: "you said fizz"
});
};
return (
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/my-screen">My Screen</Link></li>
<li><button onClick={clickHanlder}>My Screen with state</button></li>
<li><Link to="/my-screen?q=hello">My Screen with query</Link></li>
<li><Link to="/my-screen/bob">My Screen using match</Link></li>
</ul>
<div>
<button onClick={() => history.goBack()}>Back</button>
<button onClick={() => history.push("/")}>Home</button>
<button onClick={() => history.goForward()}>Forward</button>
</div>
</nav>
);
};
const MyScreen = ({
location,
match,
history,
staticContext
}: RouteComponentProps<IMyScreenRouteParams, IMyStaticContext, IHistory>) => (
<div>
<section>
<h2>Location</h2>
<p><code>location</code> has <code>IHistory</code> props.</p>
<pre><code>{JSON.stringify(location, null, 4)}</code></pre>
</section>
<section>
<h2>Match</h2>
<p><code>match</code> has <code>IMyScreenRouteParams</code> props.</p>
<pre><code>{JSON.stringify(match, null, 4)}</code></pre>
</section>
<section>
<h2>History</h2>
<p><code>history</code> has <code>IHistory</code> props.</p>
<pre><code>{JSON.stringify(history, null, 4)}</code></pre>
</section>
<section>
<h2>Static Context</h2>
<p><code>staticContext</code> has <code>IMyStaticContext</code> props or whatever static context your router has.</p>
<p>This is for a <a href="https://reacttraining.com/react-router/web/api/StaticRouter/context-object"><code><StaticRouter/></code></a>.</p>
<pre><code>{JSON.stringify(staticContext, null, 4)}</code></pre>
</section>
</div>
);
const Router = () => (
<BrowserRouter>
<div
style={{
display: "flex",
flexDirection: "row"
}}
>
<Nav />
<main>
<Route exact path="/" component={() => (<div>Click something in the <code><nav/></code></div>)} />
<Route exact path="/my-screen" component={MyScreen} />
<Route exact path="/my-screen/:id" component={MyScreen} />
</main>
</div>
</BrowserRouter>
);
export default Router;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With