Tom Dijkema
Tom Dijkema

Reputation: 33

How to navigate to another component and pass data

I am currently trying to accomplish routing from component to component and pass data so the newly opened component can use this. After some hours of research I stumbled on a few solutions that did not work in my case.

In my application I have two pages: Home and Search. The application consists of a few different layers of components, for example: App.js -> imports Home.js -> imports Body.js. App.js defines the routes by using React Router. The body of the homepage contains a search bar. When a search is executed, the application fetches data and needs to pass this data on to the Search page that will display it. Please see the files below:

App.js

import React from "react";
import {
    BrowserRouter as Router,
    Routes,
    Route
} from "react-router-dom";

/* Import Components */
import Home from "./templates/home/Home.js";
import Search from "./templates/search/Search.js";

class App extends React.Component {
    render() {
        return (
            <Router>
                <Routes>
                    <Route path="/home" element={<Home />} />
                    <Route path="/" element={<Search/>}/>
                </Routes>
            </Router>
        );
    }
}

export default App;

Home.js

import React from "react";

/* Import Components */
import Header from "../header/Header.js";
import Body from "./body/Body.js";
import Footer from "../footer/Footer.js";

class Home extends React.Component {
    render() {
        return (
            <div>
                <Header />

                <Body />

                <Footer />
            </div>
        );
    }
}

export default Home;

Body

import React from "react";
import { Container, Row, Col } from "react-bootstrap";
import './body.css';
import { Navigate } from "react-router-dom";

/* Import Components */
import TitleImage from "./titleImage/TitleImage";
import SearchBar from "./searchBar/SearchBar";
import SampleOccurrence from "./sampleOccurence/SampleOccurence";

/* Import API functions */
import SpecimenSearch from "../../../api/SpecimenSearch.js";

class Body extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            searchQuery: ''
        }
    }

    updateSearchQuery(query) {
        this.setState({
            searchQuery: query.target.value
        });
    }

    handleSearch() {
        const searchQuery = this.state.searchQuery;

        SpecimenSearch(searchQuery, process.bind(this));

        function process(result) {
            this.setState({
                searchResults: result
            });
        }
    }

    render() {
        const items = [
            'Frogger', 'Frogger', 'Frogger',
            'Frogger', 'Frogger', 'Frogger',
            'Frogger', 'Frogger', 'Frogger'
        ];

        if (!this.state.searchResults) {
            return (
                <>
                    <TitleImage />
                    <Container fluid>
                        <SearchBar
                            onSearch={() => this.handleSearch()}
                            updateSearchQuery={(query) => this.updateSearchQuery(query)}
                        />

                        <Row>
                            <Col md={{ span: "10", offset: "1" }} className="sampleOccurences">
                                <Row>
                                    <h3 className="sampleOccurencesTitle">
                                        Explore froggies
                                    </h3>
                                </Row>
                                <Row>
                                    {items.map((value, index) => {
                                        return <SampleOccurrence />
                                    })}
                                </Row>
                            </Col>
                        </Row>
                    </Container>
                </>
            );
        } else {
            // console.log(this.state.searchResults);
            // return(
                // <Navigate to="/" state={{data: this.state.searchResults}} />
                // <Search searchResults={this.state.searchResults} />
            // );
        }
    }
}

export default Body;

As you can see in Body.js, I either tried to navigate the page to Search by using the useNavigate hook and rendering a <Navigate to='/' /> element ('/' is the set Route of the search page). The hook does not work because hooks are according to React not allowed in React components. The latter works fine when linking to the Search page, but does not manage to pass on data (for example to the props of <Search />).

How can I navigate to the <Search /> page from Body.js and pass data (array of objects) by using React Router?

Upvotes: 0

Views: 6530

Answers (2)

Moath
Moath

Reputation: 578

For your goal, I wouldn't go with using react-router to pass data. I would consider using a state management library or context.

However, you can accomplish this in two ways:

  1. Functional components (with hooks) Using options.state from useNavigate()

Example

// body.js
navigate('/search', { state: {
data: [{id: 1, value: '1'}, {id: 2, value: '2'}]
} });

// search.js
const location = useLocation();
const {data} = location.state; 
// data => [{id: 1, value: '1'}, {id: 2, value: '2'}]
  1. Class components

Using options.state from <Navigate../>

Example

// body.js
 <Navigate to="/search" state={{
data: [{id: 1, value: '1'}, {id: 2, value: '2'}]
}} />

...

Upvotes: 2

Drew Reese
Drew Reese

Reputation: 202751

Tom, the passed route state will be accessible from the location object, i.e. location.state.data. You use the useLocation hook to access the location object in the receiving component.

Sender:

<Navigate to="/" state={{ data: this.state.searchResults }} />

Receiver:

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

...

const { state } = useLocation();
const { data } = state || {};

If the receiving component is a class component and can't directly use useLocation then you can create a custom withRouter Higher Order Component to handle this for you and pass location down as a prop.

Example:

import { useLocation, /* other hooks */ } from 'react-router-dom';

const withRouter = Component => props => {
  const location = useLocation();
  // other hooks
  return <Component {...props} {...{ location, /* ...other hooks */ }} />;
};

export default withRouter;

Decorate the Search component with this withRouter HOC.

export default withRouter(Search);

Now location is injected as a prop. Access the route state via this.props.location.state similar as above.

Upvotes: 1

Related Questions