Reputation: 33
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
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:
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'}]
Using options.state
from <Navigate../>
Example
// body.js
<Navigate to="/search" state={{
data: [{id: 1, value: '1'}, {id: 2, value: '2'}]
}} />
...
Upvotes: 2
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