I'm trying to pass in data that I received from my backend to another page on my frontend. I'm using react-router-dom and withRouter to pass my data from the product.js page to the result.js page on the frontend. I keep getting this error though:
Uncaught TypeError: Cannot read properties of undefined (reading 'state')
And the code always breaks on this line of code, it doesn't go past it (this code is present in the result.js page code down below):
<div className="score">hAPPi SCORE: <br/> <span>{this.props.location.state.data.data.score}</span></div>
(product.js PAGE) Here's the code of the piece in the react page that passes the received data from the backend to another page. What this code does is basically take in a user's input (through speech), send the transcript to the backend, and the backend will shoot out feedback, scores, etc..., and that data will then come back to this product.js page. From there, I want to pass that data to the result.js page.
class Product extends Component {
handleRedirect() {
// Redirect user to the results page
console.log(this.state.data)
console.log(this.state.data.data.feedback)
this.props.history.push("/results", {data: this.state.data}); // Sending backend data to frontend
}
}
export default withRouter(Product);
(result.js PAGE) Here's the code of the react page that is supposed to receive the data from the first page and display it on the page:
class ResultPage extends Component {
render() {
console.log(this.state.data) // trying to console log it, but it won't work
return (
<div className="Container">
<div className="result-section">
<div className="score">hAPPi SCORE: <br/> <span>{this.props.location.state.data.data.score}</span></div>
<div className="scale-container">
<Scale scale-id="scr-scale" style="score-scale" score={this.props.location.state.data.data.score}/>
</div>
</div>
<div className="analysis-section">
<div className="analysis-container">
<div className="analysis-title">
In-Depth Analysis
</div>
<div className="keywords-container">
<div className="section-titles">
Keywords
</div>
<div>
<ul>
{this.props.location.state.data.data.keywords.map(kw =>
<h3>{kw}</h3>
)}
</ul>
</div>
</div>
<div className="entity-container">
<div className="section-titles">
Entities
</div>
<div>
<ul>
{this.props.location.state.data.data.entities.map(en =>
<h3>{en}</h3>
)}
</ul>
</div>
</div>
<div>
<h1>Result</h1>
<div>
{this.props.location.state.data.data.feedback}
</div>
</div>
<hr></hr>
</div>
</div>
</div>
);
}
}
export default ResultPage;
I keep getting errors whenever I use the this.props.location.state.data.(something) but when I replace that with hard-coded numbers/strings, the page works fine. I'm not sure I'm doing anything wrong in terms of passing the data from the product.js page to the result.js page, but I'm pretty sure I'm doing something wrong in accessing that data from the result.js page using this.props.location....
Some help would be greatly appreciated!
You haven't shown where/how the ResultPage component is rendered. It can receive the location object as a prop in a couple ways.
Rendered directly by a Route component on one of the rendering props.
Example:
<Route path="/result" component={ResultPage} />
<Route
path="/result"
render={routeProps => <ResultPage {...routeProps} />}
/>
Decorated with the withRouter Higher Order Component.
Example:
import { withRouter } from 'react-router-dom';
class ResultPage extends Component {
...
}
export default withRouter(ResultPage);
With the route props passed/injected, the ResultPage component should now be able to access a this.props.location.state?.data value passed in route state from the product page.
Related
I am new to javascript and react, and i am trying to route from one component to another when a button is clicked.
example of html code in the sign up page:
<!-- signup button -->
<div id = "signup">
<button class="signupbutton">Sign up</button>
</div>
so when the sign up button i want it to route to this html page:
<!-- page title -->
<h1><strong>Let's get started! First up, where are you in the planning process?</strong</h1>
Any ideas on how i can do this? - i know i need to do this in javascript and with react (i ahve created a JS file for the sign up page and planning process page), but i am a bit unsure of how to do so. Any ideas?
You can't link in HTML directly with React. You need to set up two components first.
One for the page with the Button:
export default function ButtonPage () {
return (
<div id = "signup">
<button className="signupbutton">Sign up</button>
</div>
);
}
One with the page for the Get Started Page:
export default function GetStarted () {
return (
<h1><strong>Let's get started! First up, where are you in the planning process?</strong</h1>
);
}
Then You need to set up your main component, the App component and link the child components you want to display. If you use the latest version of React you need to import BrowserRouter, Route and Routes from react-router-dom:
import { BrowserRouter, Route, Routes } from "react-router-dom";
export default function App () {
return (
<BrowserRouter>
<Routes>
<Route path="/signup" element={<ButtonPage/>}></Route>
<Route path="/getstarted" element={<GetStarted/>}></Route>
</Routes>
</BrowserRouter>
);
}
Then you need to import Link from react-router-dom inside your ButtonPage Component and Link to the other Component:
import { Link } from "react-router-dom";
export default function ButtonPage () {
return (
<div id = "signup">
<Link to="/getstarted">
<button className="signupbutton">Sign up</button>
</Link>
</div>
);
}
Et voilĂ : You linked two pages in React. For more information, look up the documentation of React-Router here.
sorry if the title is not very clear.
I try to explain myself: my site is built with Next.js and retrieves data from Sanity Studio (CMS).
In Sanity the user can create menu items.
I have a menu in the footer component, one in the sidebar component and another one in the header component. The thing is, I can fetch sanity only in pages. So I created a function that, with each build, creates a JSON with all the information entered by the user.
Next, to have the menu items throughout the site, this JSON is imported into the "Layout" component and stored in a specific react context.
And everything works fine. But I noticed one thing: if I look in the source code of the page, the menu looks like this:
<nav>
<ul></ul>
</nav>
even though it is rendered perfectly in HTML.
I guess it is because I generate the JSON server side and when the page is created that information is not available.
Any ideas?
Thank you
EDIT: this is the Layout component where I imported the JSON file
import styles from '#styles/components/Layout.module.css';
import React from 'react';
//other imports...
//imported JSON
import globalData from '#client/global-manifest.json';
//this is the normalize function
import { normalizeNavigationFromRaw } from '#utils/normalizeNavigation';
//this is the navigation normalized
const navigationData = normalizeNavigationFromRaw(globalData?.navigation ?? {});
export default function Layout({ children }: { children: React.ReactElement }) {
// some functions for open/close modal ...
//context method to store navigation
const { setNavigation } = React.useContext<any>(GlobalSettingsContext);
//store navigation in context
React.useEffect(() => {
setNavigation(navigationData);
}, []);
return (
<>
<Head>
// some head code
</Head>
<main className={styles.container}>
<HeaderWrapper
toggleMenu={toggleMenu}
toggleContactPanel={toggleContactPanel}
/>
<SidebarMenu
isOpenMenu={isOpenMenu}
toggleMenu={toggleMenu}
ref={sideMenuRef}
/>
<ContactPanel
isOpenContact={isOpenContact}
toggleContactPanel={toggleContactPanel}
ref={panelRef}
/>
{children}
</main>
<Footer />
</>
);
}
the other components involved have only the navigation context imported from useContext and used. For example the footer:
import * as React from 'react';
//other imports...
import { GlobalSettingsContext } from '#contexts/GlobalSettings';
export default function Footer(): React.ReactElement {
const { navigation } = React.useContext(GlobalSettingsContext);
return (
<footer
className={
isSingleVehiclePage
? `${styles.footer} ${styles.morePadding}`
: styles.footer
}
>
<LayoutContainer>
<div className={styles.secondary}>
<div className={styles.social}>
{navigation.footerSocialIcon &&
navigation.footerSocialIcon.map((el: any, mainKey: number) => (
<Link key={mainKey} to={el.titleLink ?? ''}>
<span className={`icon-${el.iconClass ?? ''}`}></span>
</Link>
))}
</div>
</div>
</LayoutContainer>
</footer>
);
}
From the provided description i assume you are using getStaticProps. (although the it would be basically the same issue for getServerSideProps)
In order for data fetched to be pre-rendered in html, you need to pass it as props, returning it from getStaticProps.\
What you are doing is passing data to a react context, which is rendered after hydration takes place.
I advise you to review the basics of nextjs to understand what code is executed in the client and which is executed in the server, along with how pre-rendering works.
please check https://nextjs.org/docs/basic-features/pages
I have 3 components in nextjs and i want to achieve the below snippet in nextjs
<Route path="/" component={homePage} />
<Route path="/about" component={aboutPage} />
<Route path="/faq" component={faqPage} />
Q1. How can i do the same in nextjs without page refresh? (without react-router)
(Edit : some scholars are suggesting to read the docs but i have read it thoroughly and what i want is to pass a component along with the route)
Is this even possible in next js?
Q2: If i have url as /products?product_id=productid and on refresh if i want the url to be /products (basically i want to remove all params on refresh) What is the best practice to do this?
Thanks in advance
NextJS functions on a convention-based filesystem-based routing. You'd need to place your components in a directory structure that matches the routes you are wanting.
More details here:
https://nextjs.org/docs/routing/introduction
The Next.js docs don't really cover how to change away from <Route> components, however they have a lot of examples as code on how to do most things with Next.js. https://github.com/vercel/next.js/tree/canary/examples/layout-component
The below is what I used as an alternative to the component (there's no direct Next.js alternative).
_app.js
export default function MyApp({ Component, pageProps }) {
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout || ((page) => page)
return getLayout(<Component {...pageProps} />)
}
Any page:
import Layout from '../components/layout'
import Sidebar from '../components/sidebar'
export default function About() {
return (
<section>
<h2>Layout Example (About)</h2>
<p>
This example adds a property <code>getLayout</code> to your page,
allowing you to return a React component for the layout. This allows you
to define the layout on a per-page basis. Since we're returning a
function, we can have complex nested layouts if desired.
</p>
<p>
When navigating between pages, we want to persist page state (input
values, scroll position, etc) for a Single-Page Application (SPA)
experience.
</p>
<p>
This layout pattern will allow for state persistence because the React
component tree is persisted between page transitions. To preserve state,
we need to prevent the React component tree from being discarded between
page transitions.
</p>
<h3>Try It Out</h3>
<p>
To visualize this, try tying in the search input in the{' '}
<code>Sidebar</code> and then changing routes. You'll notice the input
state is persisted.
</p>
</section>
)
}
About.getLayout = function getLayout(page) {
return (
<Layout>
<Sidebar />
{page}
</Layout>
)
}
The main part for the layout that you want to wrap around the pages, components/layout.js:
import Head from 'next/head'
import styles from './layout.module.css'
export default function Layout({ children }) {
return (
<>
<Head>
<title>Layouts Example</title>
</Head>
<main className={styles.main}>{children}</main>
</>
)
}
What's happening is the _app.js wraps all pages inside the declared layout. Each page then defines what layout that page belongs to. The layout then accepts a page as the {children} prop object of which you can then render anywhere in your layout page.
Next uses filesystem based routing, your folder structure should look like
-- pages
-- index.js
-- about/index.js
-- faq/index.js
For the custom component part, make a component that's clickable, on click, use next builtin router to redirect
const router = useRouter();
router.push('/');
I've created a very simple Next.js-project with two pages.
Both pages include a basic layout component:
// Page Component
render() {
return (
<PageLayout>
This is page A
</PageLayout>
);
}
And PageLayout looks something like this:
// PageLayout
render() {
return (
<div>
<Header />
{this.props.children}
</div>
);
}
So both pages use PageLayout to render a basic template that also includes the same Header on both pages.
My problem is that the Header component is re-created when navigating between the two pages. I think this is bad not only from a performance point of view, but also because in this case all DOM-Nodes and all React components loose their local state.
So I would like to know if there is something I am missing or how we can create shared components between pages that are reused properly (at least if their state did not change, of course).
You have Two pages with common component:
Page A:
<A>
<Common />
</A>
Page B:
<B>
<Common />
</B>
From the React documentation:
Whenever the root elements have different types, React will tear down
the old tree and build the new tree from scratch. Going from <a> to
<img>, or from <Article> to <Comment>, or from <Button> to <div> - any
of those will lead to a full rebuild.
This is why you lose the state in Common (Header) component. Like I suggested in the comment you would have to use an external state like redux.
You have to create a component with the name of layout
// components/layout.js
import Navbar from './navbar'
import Footer from './footer'
export default function Layout({ children }) {
return (
<>
<Navbar />
<main>{children}</main>
<Footer />
</>
)
}
And then rap your _app.js component like this
// pages/_app.js
import Layout from '../components/layout'
export default function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}
for more read https://nextjs.org/docs/basic-features/layouts
I have set up a basic react app with hash navigation. Problem is when I click on any link to navigate between pages, I see that the hash in the url is changing properly, as well as I added a console.log in my layour's render to see if it's getting called and it is, with proper this.props.children values, however the page is not rendering anything. If I go to any route and refresh the page I see the correct components rendered, but if I navigate somewhere from there noting gets rendered until I refresh again.
import React from "react";
import ReactDOM from "react-dom";
import { IndexRoute, Router, Route, Link, hashHistory as history } from 'react-router';
class Layout extends React.Component {
render() {
console.log(this.props, document.location.hash);
return <div>
<div>
<span>LEYAUTI MLEAYTI {Math.random()}</span>
</div>
<div>
{this.props.children}
</div>
<div>
{this.props.params.project}
</div>
</div>
}
}
class CreateProject extends React.Component {
render() {
return <div>
<h1>Create PROEKT</h1>
</div>
}
}
class Projects extends React.Component {
render() {
return <div>
<h1>PROEKTI MROEKTI</h1>
<Link to="/projects/create">New project</Link>
</div>
}
}
ReactDOM.render(
<Router history={history}>
<Route path="/" component={Layout}>
<IndexRoute component={Projects}/>
<Route path="projects/create" component={CreateProject}/>
</Route>
</Router>,
document.getElementById('app-root'));
Here is a visual of what's happening in the console when I navigate on a couple routes, but the DOM remains unchanged
This may be an issue with hashHistory. Which react-router version are you using? With v4 and above, you need to use history like so -
import createHistory from 'history/createHashHistory'
const history = createHistory()
// pass history to the Router....
Your component didn't actually unmount/remount if you only update your hashtag in your url. The route however, is updated. So you can only see the component loads content for once when you refresh the page.
You will need to create state variables and update it in a routeChange handler callback and bind the updated state variable to your view by using setState. Then the component can get updated.
See this post for how to add the route change listener (https://github.com/ReactTraining/react-router/issues/3554)
Alright, so I got down to the bottom of it.
The problem was that I was including my client.min.js file before the default app.js file of laravel 5.4's default layout. For some reason it broke react in a very weird way. What I had to do is switch the order in which the two files were included.