Designing persistent layouts in Next.js - javascript

I'm going through this article and I'm trying to figure out how the persistence is supposed to occur in Option 4. From what I can tell, you'd need to redefine the .getLayout for every page. I'm not sure how the logic for nesting is incorporated into further urls.
Here's the code from the article
// /pages/account-settings/basic-information.js
import SiteLayout from '../../components/SiteLayout'
import AccountSettingsLayout from '../../components/AccountSettingsLayout'
const AccountSettingsBasicInformation = () => <div>{/* ... */}</div>
AccountSettingsBasicInformation.getLayout = page => (
<SiteLayout>
<AccountSettingsLayout>{page}</AccountSettingsLayout>
</SiteLayout>
)
export default AccountSettingsBasicInformation
// /pages/_app.js
import React from 'react'
import App from 'next/app'
class MyApp extends App {
render() {
const { Component, pageProps, router } = this.props
const getLayout = Component.getLayout || (page => page)
return getLayout(<Component {...pageProps}></Component>)
}
}
export default MyApp
For example, say AccountSettingsBasicInformation.getLayout is /settings/, how would I use this template to produce something at /settings/username
P.S. If someone has done something in the past they'd recommend over this, I'm open to ideas.

Yes, you have to redefine the getLayout function to every page. As long as the SiteLayout component stays “unchanged” (eg.no props change) the rendered content in that layout component (not the page content itself) stays persistent. This is because React wont rerender that component.
I used Adam’s article when I was building next.js lib for handlin modal routes. You can check the example folder where you can see I am defining the getLayout property on every page which should be rendered with layout.
Example: https://github.com/svobik7/next-bodies/tree/master/example

Related

How to route class component by clicking button in React?

I am new to React. I am trying to build a page and having like 3 button or img on main page. When I click either one, I shall be routed to another class component. You can treat it like click the icon and route you to another category page (just an example). Below is my structure and partial code I tried. I have no idea how to achieve that, and I googled and seems cannot find the stuff I want.
Structure:
/src
.index.js
.App.js
.BookStore.js
.FruitStore.js
.FoodStore.js
index.js
import React from "react";
import { render } from "react-dom";
import App from "./App";
render(
<App />,
document.getElementById('root')
);
App.js:
import React from "react";
import BookStore from "./BookStore";
const AppContainer = () => {
return (
//do the routing
<BookStore/>
)
};
export default AppContainer;
BookStore.js
export default class BookStore extends React.Component {
}
const contentDiv = document.getElementById("root");
const gridProps = window.gridProps || {};
ReactDOM.render(React.createElement(BookStore , gridProps), contentDiv);
First, you could have a look at the/one react router, e.g. https://reactrouter.com/web/guides/quick-start
However, since you're writing you're new to react, this might be a little too much ...
First, I was wondering why you're using the "ReactDOM" in your indexjs (that seems to be correct), but also in the BookStore.js. I would also recommend to write your components as functions, like your "AppContainer" and not use the class components anymore (or do you really need to do that? - why?). You can use hooks instead to have e.g. state in the components.
You would then need any kind of state in your AppContainer which is used for the routing. Maybe like this:
const AppContainer = () => {
const [showDetail, setShowDetail] = useState();
return <>
{!showDetail && <BookStore onDetail={detail => setShowDetail(detail)} />}
{showDetail && <DetailPage detail={showDetail} onBack={() => setShowDetail(undefined)}}
</>
}
Your AppContainer then has a state wheter or not to show the Bookstore (which is shown when "showDetail" is falsy, or a DetailPage which is shown when showDetail is truthy.
For this to work, your Bookstore needs to provide callbacks to let the AppContainer know that something should change. Very simply it could look like this:
const BookStore = ({onDetail}) => {
return <button onClick={() => onDetail("anything")}>Click me</button>
}
Now when someone clicks the button on the bookstore, it calls the "onDetail" callback, which was set in the AppContainer to set the "showDetail" state. So this one will be updated to "anything" in this case. This will result in a rerender on the AppContainer which will now render a DetailPage component instead.

Using static html/css/jquery landing pages in ReactJS project

so I had an idea of generating various "landing pages" for my project that would use similar elements with different data.
The idea is to essentially to have my react app (create-react-app) load a "container component" and would then display static landing pages.
For example files are...
screenContainer.jsx
pageOne.html
pageTwo.html
Depending on the user's choice I can render pageOne.html or page2.html. These pages are "templates" which work with jquery css, etc.
Is this possible? If so, what is the best approach.
I'm using html-loader now but am stuck at this:
./src/landingPages/serre/assets/js/plugins/jquery/jquery-3.3.1.min.js
Line 2:1: Expected an assignment or function call and instead saw an expression no-unused-expressions
Any ideas?
Thanks!
Here is my current file react screen container component:
require("es6-object-assign/auto");
import React, { Component, Fragment } from "react";
import $ from "jquery";
import Page from "../landingPages/serre/index.html";
var ReactDOMServer = require("react-dom/server");
var HtmlToReactParser = require("html-to-react").Parser;
var htmlDoc = { __html: Page };
class CreatorLandingPageContainer extends Component {
constructor(props) {
super(props);
}
render() {
return <Fragment>HELLO{this.renderPage()}</Fragment>;
}
renderPage() {
var htmlToReactParser = new HtmlToReactParser();
var reactElement = htmlToReactParser.parse(htmlDoc);
return reactElement;
}
}
export default CreatorLandingPageContainer;

Persist data between two pages with Next.js

I would like to refactor my Next.js webapp to have different pages handle different screens. Currently, I have this component holding several states to know in which screen I'm in. In the jsx section, I'm using {value && ... } to render the right component.
But I feel this is not good design, and won't be maintainable when adding more and more screens.
I would also like to avoid Redux as it is overkill for my project.
I was thinking about persisting data in cookies so I can retrieve them with getInitialProps in every component when rendering a new page, but is there a more elegant way?
I've read about tweaking the _app.js but I'm not sure to understand the consequences of doing so, and how it could help me..
Any suggestion?
When multiple of your pages need to make use of same data, you can make use of Context to store the result. It a good way to make a centralized storage without using complex and more self sufficient libraries like redux
You can implement context inside of _app.js file which must reside inside your root folder. This way next.js treats it as a root wrapper and you would just need to use 1 instance of Context
contexts/appContext
import React from 'react';
const AppContext = React.createContext();
export const AppProvider = AppContext.Provider;
export const AppConsumer = AppContext.Consumer;
export default AppContext;
_app.js
import React from 'react'
import App from 'next/app'
import AppProvider from '../contexts/appContext';
class MyApp extends App {
state={
data:[]
}
render() {
const { Component, pageProps } = this.props;
// You can implement logic in this component to fetch data and update state
return (
<div>
<AppProvider value={this.state.data}> // pass on value to context
<Component {...pageProps} />
</AppProvider>
</div>
)
}
}
export default MyApp
Now further each component can make use of context value by using AppConsumer or using useContext if you use hooks
Please read more about how to use Context here

Next.js: Render dynamic pages customized based on requesting host

I want to render dynamic next.js pages with custom content / style based on the domain which requests the page.
Basically run one next.js app under multiple domains.
I know that I have to do some sort of custom routing, but don't know exactly how and how I can pass the host information to the requested page, so it fetches the matching data from a database.
You should be able to verify this in your pages/_app.jsx file static getInitialProps(context) method and use the context to verify where the request is coming from then return a flag to the component.
Example:
// pages/_app.js
import app from 'next/app';
export default class MyApp extends app {
static getInitialProps({ Component, router, ctx }) {
let pageProps = app.getInitialProps({ Component, router, ctx });
if (ctx.req.headers.host.match(/localhost/)) {
pageProps = {
...pageProps,
renderFrom: 'mydomain',
}
}
return { pageProps };
}
render() {
const { Component, pageProps } = this.props;
return (
<section id="app">
<Component {...pageProps} />
</section>
);
}
}
Note that I call app.getInitialProps to mimic the next/app component behaviour as in the source code here
In your pages/index.js you will have access to props.renderFrom and there you can display data.
// pages/index.js
import React from 'react'
const Home = props => (
<div>
Rendering content for domain: {props.renderFrom}
</div>
)
export default Home
Since you're verifying where the request comes from in _app.js this property will be available in all your containers (pages) so you can have the same routing for your application and just render data differently.
Tip: You could also use next-routes to manage the routing for the application, it's better than the out next comes with, and in here you have a custom server where you can manage your traffic as well.
Hopefully this helps you and points you in the right direction.
Here’s an example of hosting multiple domains on the same Next.js site (while maintaining multiple languages and static site generation/SSG), using Next.js’ i18n system:
https://github.com/tomsoderlund/nextjs-multi-domain-locale

Use react without a router component

If I want to make a web application using reactjs that is not a single page.
Should I compile all the react code into a single file and load it on all pages of the application, then use the function that I expose to render the necessary components?
Example of an html file
<div id="Clock" data-react="Clock"></div>
<div id="HelloWorld" data-react="HelloWorld"></div>
example of index.js
import React from 'react';
import ReactDOM from 'react-dom';
import Clock from './Clock';
import HelloWorld from './HelloWorld';
import OtherComponent from './OtherComponent';
const APPS = {
Clock,
HelloWorld,
OtherComponent
};
const MyReactRender = react => {
let component = react.getAttribute('data-react');
let App = APPS[component];
if(App != undefined) {
ReactDOM.render(<App />, document.getElementById(component));
}
}
document.querySelectorAll('[data-react]').forEach(MyReactRender);
I'd see two ways, of increasing quality and difficulty. In both cases, you use good old anchors elements to redirect the page to a url, to which different templates correspond.
Manually check for the existence of divs id's
In this case, each template includes the same javascript bundle that contains everything in the app and a single element with an id corresponding to the specific component. The idea is to check wether or not an element is present in the page, then activate its corresponding react component if it is.
if (document.getElementById('component-root')) {
ReactDOM.render(<Component />, document.getElementById('component-root'));
}
On the up side, it's quite easily implemented. On the down side, the bundle will always get bigger and bigger, and the list of ifs grows each time you add a new "page".
Separate your modules in actual bundles
Different bundle managers exist, but I'd recommend using Webpack to create multiple bundles that contain only specific part of your application. Then, each template contains only the corresponding div element, as well as that specific bundle.
<head><script src="/js/clock.js"></head>
<body><div id="root-clock"></div></body>
<head><script src="/js/otherComponent.js"></head>
<body><div id="root-other-component"></div></body>
How to package multiple bundles with webpack is out of the scope of this answer, but look here.
I've tried making a react application without a router. I used ternary operators to switch from component to component.
// App Component
class App extends Component {
constructor(props) {
super(props)
this.state = {
inClockComponent: true,
inHelloWorldComponent: false,
inOtherComponent: false
}
}
render() {
const {inClockComponent, inHelloWorldComponent, inOtherComponent} = this.state
return (
<div>
{
inClockComponent
? <Clock> : inHelloWorldComponent
? <HelloWorld> : inOtherComponent ? <OtherComponent> :
<div>No Component Here</div>
}
</div>
}
You could pass a function from the App component that would change the display state to each child component of App
Example
// in App Component
showHelloWorldComponent() {
this.setState({
inClockComponent: false,
inHelloWorldComponent: true,
inOtherComponent: false
)}
}
You insert that function onto a button that would navigate to a different component
Example
// in Clock Component
render() {
return (
<div>
<h2>Time is 5:15 P.M.</h2>
<button onClick={this.props.showHelloWorldComponent}>
Go To Hello World
</button>
)
}
It's a messy solution, and I wouldn't suggest using it in a big application, but I hope this answers your question!

Categories

Resources