2

I am using react-router-dom v^6.0.0-beta.1 with React v^17.0.1 and I am navigating from 1 page to another by clicking on a card out of a list of cards:

PlayersPage.tsx

/** @jsxImportSource @emotion/react */
import React, { useState, useEffect } from 'react';
import { Page } from './Page';
import { PageTitle } from './PageTitle';
import { Button, Card } from 'react-bootstrap';
import { css } from '@emotion/react';
import pic from '../assets/samurai.png';
import { useNavigate } from 'react-router';
import { Link } from 'react-router-dom';

function PlayersPage() {
  interface PlayersDataType {
    id: number;
    handle: string;
    role: string;
    avatar: string;
    specialAbilities: {
      playerId: number;
      authority: number;
      charismaticLeadership: number;
      combatSense: number;
      credibility: number;
      family: number;
      interface: number;
      juryRig: number;
      medicalTech: number;
      resources: number;
      streedeal: number;
    };
    stats: {
      playerId: number;
      int: number;
      ref: number;
      tech: number;
      cool: number;
      attr: number;
      luck: number;
      ma: number;
      body: number;
      emp: number;
      run: number;
      leap: number;
      lift: number;
    };
  }

  //const [isLoading, setIsLoading] = useState<boolean>(false);
  const [playersData, setPlayersData] = useState<Array<PlayersDataType>>([]);
  const navigate = useNavigate();

  const createNew = () => {
    navigate('../Create');
  };

  const handleSelect = () => {
    navigate('../Player');
  };

  //Fetch all players from database
  useEffect(() => {
    //setIsLoading(true);
    fetch('https://localhost:44326/api/Players/all')
      .then((response) => {
        if (response.ok) {
          return response.json();
        }
      })
      .then((data) => setPlayersData(data));
    //.then(setIsLoading(false));
  }, []);

  return (
    <Page>
      <div>
        <PageTitle>Toons</PageTitle>
        <div
          css={css`
            justify-content: center;
            margin-left: 255px;
            margin-bottom: 10px;
          `}
        >
          <button onClick={createNew}>Create New</button>
        </div>
      </div>

      <div //Every card is listed in 1 column
        css={css`
          flex-direction: column;
          margin-left: 40px;
        `}
      >
        {playersData.map((data) => (
          <div key={data.id}>
            <Card
              css={css`
                display: flex;
                width: 500px;
                height: 200px;
                flex-direction: column;
                //border: 2px solid black;
                box-shadow: 0 3px 7px 0 rgba(30, 38, 46, 0.21);
                cursor: pointer;
                :hover {
                  transform: scale(
                    1.2
                  ); /* (150% zoom - Note: if the zoom is too large, it will go outside of the viewport) */
                }
              `}
              onClick={handleSelect}
            >
              <div
                css={css`
                  display: flex;
                  flex-direction: row;
                  //border: 2px solid orange;
                `}
              >
                <Card.Img
                  src={pic}
                  alt="No photo found."
                  css={css`
                    margin: 5px 10px 10px 10px;
                    width: 150px;
                    height: 150px;
                    border-radius: 50%;
                    box-shadow: 0 3px 7px 0 rgba(30, 38, 46, 0.21);
                    //border: 2px solid black;
                  `}
                />
                <Card.Body
                  css={css`
                    flex: 1;
                    flex-direction: row;
                    //border: 2px solid purple;
                  `}
                >
                  <div
                    css={css`
                      display: flex;
                      flex-direction: row;
                      //justify-content: space-between;
                      flex-wrap: wrap;
                    `}
                  >
                    <Card.Text>Int:</Card.Text>
                    <Card.Text>{data.stats.int}</Card.Text>
                    <Card.Text>Ref:</Card.Text>
                    <Card.Text>{data.stats.ref}</Card.Text>
                    <Card.Text>Tech:</Card.Text>
                    <Card.Text>{data.stats.tech}</Card.Text>
                    <Card.Text>Cool:</Card.Text>
                    <Card.Text>{data.stats.cool}</Card.Text>
                    <Card.Text>Attr:</Card.Text>
                    <Card.Text>{data.stats.attr}</Card.Text>
                    <Card.Text>Luck:</Card.Text>
                    <Card.Text>{data.stats.luck}</Card.Text>
                    <Card.Text>Ma:</Card.Text>
                    <Card.Text>{data.stats.ma}</Card.Text>
                    <Card.Text>Body:</Card.Text>
                    <Card.Text>{data.stats.body}</Card.Text>
                    <Card.Text>Emp:</Card.Text>
                    <Card.Text>{data.stats.emp}</Card.Text>
                    <Card.Text>Run:</Card.Text>
                    <Card.Text>{data.stats.run}</Card.Text>
                    <Card.Text>Leap:</Card.Text>
                    <Card.Text>{data.stats.leap}</Card.Text>
                    <Card.Text>Lift:</Card.Text>
                    <Card.Text>{data.stats.lift}</Card.Text>
                  </div>
                </Card.Body>
              </div>
              <Card.Title
                css={css`
                  display: flex;
                  width: 150px;
                  flex-direction: row;
                  justify-content: center;
                  font-weight: bold;
                  margin-bottom: 10px;
                `}
              >
                {data.handle}, {data.role}
              </Card.Title>
            </Card>
            <br></br>
          </div>
        ))}
      </div>
    </Page>
  );
}

export default PlayersPage;

I would like to send the data.id of whichever card that the user clicks on.

Is there any way I can send data.id as a param here with the onClick function:

  <div key={data.id}>
            <Card
              css={css`
                display: flex;
                width: 500px;
                height: 200px;
                flex-direction: column;
                //border: 2px solid black;
                box-shadow: 0 3px 7px 0 rgba(30, 38, 46, 0.21);
                cursor: pointer;
                :hover {
                  transform: scale(
                    1.2
                  ); /* (150% zoom - Note: if the zoom is too large, it will go outside of the viewport) */
                }
              `}
              onClick={handleSelect}
            >

and somehow send it here:

 const handleSelect = () => {
    navigate('../Player');
  };

and then use that id on another page?

EDIT:

This is what I am trying now:

onClick={() => handleSelect(data.id)}

which I believe is correct. Then in the handleSelect function:

 const handleSelect = (id: number) => {
    history.push(pathname: '/Player',
      state: {  
        id: id, 
      },)
  }

However I am getting 3 errors on this: Property 'push' does not exist on type 'History'.ts(2339) and Cannot find name 'pathname'.ts(2304) and same error for state.

EDIT 2:

I have added const history = useHistory(); and updated the handleSelect function as follows:

 const handleSelect = (id: number) => {
    history.push({
      pathname: '/Player',
      state: {
        id: id,
      },
    });
  };

Everything seems fine except the typescript error Module '"react-router-dom"' has no exported member 'useHistory'.ts(2305).

GrillOwner69420
  • 645
  • 2
  • 5
  • 24
  • You can use useHistory hook. check this : [enter link description here](https://stackoverflow.com/questions/59464337/how-to-send-params-in-usehistory-of-react-router-dom) – walidum Aug 23 '21 at 20:07

2 Answers2

2

In your handleSelect method you need to pass the data.id param.

onClick={handleSelect(data.id)}

And take care of it in your handler. But you have to wrap the function like this.

const handleSelect = (clickEvent) => (dataId) {
    //dataId will be available to use here
    navigate('../Player/');
};

Then I would have a look at the useHistory hook as the history prop is considered to be legacy.

import React from 'react';
import { useHistory } from 'react-router-dom';

const Hello = () => {
  const history = useHistory();
  const handleClick = () => history.push('/goodbye');

  return (
    <button type="button" onClick={handleClick}>
      Goodbye
    </button>
  );
};

export default Hello;

So when you push to the history URL you can create a route that uses that parameter. Something like this:

history.push('/yourURL/:dataId');

Then on that page, you can pull that parameter down and your app there can consume it.

daHolby
  • 171
  • 2
  • 6
  • using `onClick={handleSelect(data.id)}` is not correct, you should change it to `onClick={()=>handleSelect(data.id)}` – Abbasihsn Aug 23 '21 at 20:49
1

use this:

onClick={()=>handleSelect(data.id)}

and modify your handleSelect as follow:

const handleSelect = (id) => {
    this.props.router.push({
      pathname: '/pathname',
      state: {
        id: id
      }
    })
  };

or use useHistory:

const handleSelect = (id) => {
    history.push(pathname: '/YOUR PATH',
      state: {  
        id: id, 
      },)
  }


for your edit section:

  1. import useHistory and use it:
import { useHistory } from "react-router-dom";

// in your component define a history variable
const history = useHistory();

// then use it whenever you want:
history.push({
  pathname: '/your path', //change the value to whatever you want
  state: {  
    id: id, // you can add other variables as well
  },
}); 
  1. now you can access this variable in your destination component: in class component:
this.props.location

in functional component:

import { useLocation } from 'react-router-dom';

const location = useLocation();
let id  = location.state.id;
Abbasihsn
  • 2,075
  • 1
  • 7
  • 17
  • On the first method I get an error on id: ````Parameter 'id' implicitly has an 'any' type.ts(7006)```` and on this: ````'this' implicitly has type 'any' because it does not have a type annotation.ts(2683) PlayersPage.tsx(11, 10): An outer value of 'this' is shadowed by this container.```` – GrillOwner69420 Aug 23 '21 at 20:15
  • 1
    the first method is for class components! if you use a functional component you should use `useHistory`, have you defined useHistory? you should use it as follow: `const history = useHistory();` – Abbasihsn Aug 23 '21 at 20:19
  • Ok with the history method I get these error: ````Property 'push' does not exist on type 'History'.ts(2339)```` , ````Cannot find name 'pathname'.ts(2304)````, and ````Cannot find name 'state'.ts(2304)````. – GrillOwner69420 Aug 23 '21 at 20:26
  • I edited my question to show where I'm at. – GrillOwner69420 Aug 23 '21 at 20:30
  • I get the error: ````Module '"react-router-dom"' has no exported member 'useHistory'.ts(2305)```` I have run yarn install and yarn add @types/react-router-dom but the issue persists – GrillOwner69420 Aug 23 '21 at 20:44
  • I updated my question again, but that typescript error is the only issue now. – GrillOwner69420 Aug 23 '21 at 20:50
  • see https://stackoverflow.com/questions/44420631/react-router-link-typescript-error and https://github.com/DefinitelyTyped/DefinitelyTyped/issues/14062 – Abbasihsn Aug 23 '21 at 21:07
  • Ok the second one did fix the issue, but now I get the exact same error on useNavigate instead... – GrillOwner69420 Aug 23 '21 at 21:15
  • That actually caused a lot of my other imports to develop the same issue. I'm going to have to just open another question to try to fix typescript issues. Thank you for the help. – GrillOwner69420 Aug 23 '21 at 21:20
  • Hey so react-router-dom version 6 does not have useHistory, how can I do this without it? – GrillOwner69420 Aug 23 '21 at 23:09
  • I did not know that, see https://stackoverflow.com/questions/63471931/using-history-with-react-router-dom-v6 – Abbasihsn Aug 24 '21 at 08:09
  • yes that's how I was navigating to begin with. But I need to pass params with it. I think I know how to do this but I'm getting too many typescript errors to test. I have opened a question for this if you know anything about typescript: https://stackoverflow.com/questions/68907952/react-router-dom-version-6-usenavigate-argument-of-type-whatever-number – GrillOwner69420 Aug 24 '21 at 13:07