I am using React. I am using class Components.
I am also using React Router.
Here is my code:
constructor(props){
super(props);
}
handleCreateComponent = () =>{
return this.props.history.push('/app/pages/pageA/new');
}
render = () =>{
return(
<div>
<div>
<Button id="addComponent" onClick={this.handleCreateComponent}>ButtonA</Button>
</div>
</div>
);
}
My Question:
When I click "ButtonA", it navigates to the link: /app/pages/pageA/new
However, the page is empty and not displaying anything even though it's supposed to render.
What might be the problem ?
You should use <Link to="/app/pages/pageA/new"/> or <NavLink to="/app/pages/pageA/new"/> from the react-router instead of <Button/>
But the rendering problem might be something else. in your question you say /api/pages/pageA/new but in the code it's '/app/pages/pageA/new'
Related
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 am using React Router.
I want when the user clicks on the button, it directs them to the page (endpoint) /form which has the UserForm component.
Here is my code wrapping the button:
<Router>
<Link to="/form" className="updateLink">
<button className="updateBtn" onClick={() => {
this.update(id);
console.log(`Item Number: ${id} Was Updated Successfully`);
window.alert(`Item Number: ${id} Was Updated Successfully`);
}}>U</button>
</Link>
<Switch>
<Router exact path="/form" component={UserForm} />
</Switch>
</Router>
So the reason that doesn't work is because the Button has its own click handler separate from the link handler. You have two options available to you:
Style the Link tag such that it looks like a button but don't actually look like a button (this won't work if you need to do additional logic in addition to routing)
Actually use a button. And then use the 'useHistory' React Hook React Router provides to get the functionality you're looking for.
The component would look something like this:
const history = useHistory()
return (
<Button onClick={() => history.push("/abc")}/>
)
I would personally recommend that you simply style the link tag in the way that you need it to. As that would be more accessible and understandable to the user. But that's only a good idea if you only care about the routing.
Is the above code accurate as in your code?
Router statement should be set up as below, usually in App.js
<Router>
<Switch>
<Route path = './form' component = {form}
</Switch>
</Router>
You then create the form component and to link to it you then import the link component in the component you wish to use it.
Then use as below
<Link to = './form'> Some Text </Link>
Onto the button issue you are having
It will render but you shouldn't nest an <a> or <button> tag in HTML as it wont be sematic for screenreaders, isn't accessible, nor it it valid HTML.
The Link element in react creates and renders an <a> tag which shouldn't be nested in a button.
You could use useHistory in your case for the same effect
import React from 'react'
import { useHistory } from 'react-router-dom'
const component = () => {
const { push } = useHistory()
...
<button
type="button"
onClick={() => push('/form')}>
Click me to go to a form!
</button>
...
}
I have a React component:
class Control extends React.Component {
constructor(props) {
super(props);
this.handleSaveFile = this.handleSaveFile.bind(this);
this.handleExecFile = this.handleExecFile.bind(this);
}
handleSaveFile(e) {
...
}
handleExecFile(e) {
...
}
render() {
return (
<div id={this.props.name} className="Control" >
<button onClick={this.handleSaveFile}>Save</button>
<button onClick={this.handleExecFile}>Exec</button>
</div>
)
}
}
After building the project with npm run build, the application displays correctly, but the "control" component HTML has been reduced to:
<div id="control" class="Control">
<button>Save</button>
<button>Exec</button>
</div>
In other words it appears as if the "onClick" has been optimized away.
I am uncertain why that would happen?
I am new to React.js and I am new to front-end development, any help appreciated.
After working with the application a bit longer I see what is happening.
Indeed there is no "onClick" rendered in the HTML of the React component, but the component does actually respond to clicks. The onClick is just being registered in a different way by the framework.
This correct behavior of our build tool. Check your javascript file. Your handlers are placed at the javascript bundle.
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.