Search code examples
reactjstypescriptroutescomponents

How do I make a route change in React using Typescript?


I am coding along the ZTM course, making a face recognition app, but instead of Javascript, I am using Typescript to build my app, avoiding using any type. I am new to the language and don't fully grasp its nuances, so please advise me on how to resolve the issue below.

The problem is simple. I want the Sign Out page to appear when we click the corresponding button. However, I can't make this task possible like it's made in Javascript.

Let me show you what my code looks like to make it simpler to understand the problem. Firstly, there is my main App component, including its interface and parts that are important to the issue:

interface IAppState {
  input: string,
  imageUrl: string,
  box: Object,
  route: string
}

class App extends Component<{title: string}, IAppState> {
  constructor(props: {title: string}) {
    super(props);
    this.state = {
      input: '',
      imageUrl: '',
      box: {},
      route: 'signin'
    }
  }

Here is the part of the main App component that makes the state change and routes us to the main page, including the render() method, where you can see my property onRouteChange:

onRouteChange = ({route}) => {
    this.setState({route: route});
  }

  render() {
    return (
      <div className="App">
        <ParticlesBg type="cobweb" bg={true} />
        <Navigation onRouteChange={this.onRouteChange} />
        { this.state.route === 'signin'
          ? <SignIn onRouteChange={this.onRouteChange}/>
          : <>
              <Logo />
              <Rank />
              <ImageLinkForm title='image link form'
              onInputChange={this.onInputChange}
              onButtonSubmit={this.onButtonSubmit}
              />
              <FaceRecognition imageUrl={this.state.imageUrl}/>
            </>
        }
      </div>
    );
  }

In my Navigation component, there is the Sign Out button, which should route us back to the Sign In page:

interface INavigationProps {
    onRouteChange: MouseEventHandler<HTMLInputElement>;
}

const Navigation: FC<INavigationProps> = ({ onRouteChange }) => {
    return (
        <nav style={{ display: 'flex', justifyContent: 'flex-end' }}>
            <p onClick={() => onRouteChange('signin')} className='f3 link dim black underline pa3 pointer'>Sign Out</p>
        </nav>
    );
}

With a given code the Sign Out button doesn't work. Errors appear.

The first one that's obvious to me is regarding the route element: Binding element 'route' implicitly has an 'any' type.

And the second one that I don't understand how to fix:

Type '({ route }: { route: any; }) => void' is not assignable to type 'MouseEventHandler<HTMLInputElement>'.
  Types of parameters '__0' and 'event' are incompatible.
    Property 'route' is missing in type 'MouseEvent<HTMLInputElement, MouseEvent>' but required in type '{ route: any; }'.

The last error is in my Navigation component: Argument of type 'string' is not assignable to parameter of type 'MouseEvent<HTMLInputElement, MouseEvent>'.

I was playing with interfaces and types to resolve this, but I couldn't find the solution. How do I make the Sign Out button route us back to the Sign In page?


Solution

  • I have done more research and here's the final error-free code below. Firstly, fixed the line in interface IAppState to:

    route: MouseEventHandler<HTMLInputElement> | undefined | string
    

    Then redacted onRouteChange to look like this, stating its types accordingly:

    onRouteChange = (route: MouseEventHandler<HTMLInputElement> | undefined | string) => {
        return this.setState({route: route});
      }
    

    And then final changes were made to the Navigation component:

    interface INavigationProps {
        onRouteChange: (route: MouseEventHandler<HTMLInputElement> | undefined | string) => void;
    }
    
    const Navigation: FC<INavigationProps> = ({ onRouteChange }) => {
        return (
            <nav style={{ display: 'flex', justifyContent: 'flex-end' }}>
                <p onClick={() => onRouteChange('signin')} className='f3 link dim black underline pa3 pointer'>Sign Out</p>
            </nav>
        );
    }
    

    That way I can access Sign In page from the home page, using Sing Out button.