Next.js 13 (app dir) - Access page metadata in Layout - javascript

I am using the new App folder structure with Next.js 13 project, and I would like to display a subheader in the RootLayout containing the value of the Page title (I'm using metadata on each page to configure the head title).
Is there any way to directly access that value with SSR?
Example layout.js:
export default function RootLayout({ children) {
const pageTitle = "???"; // How to get this value?
return (
<html>
<body>
<HeaderComponent />
<div className="subheader">
<h3>{pageTitle}</h3> <------ HERE
</div>
<div className="content">{children}</div>
<FooterComponent />
</body>
</html>
)
);
Example page.js:
export const metadata = {
title: "Login"
};
export default function Login() {
return (
<div>Login form</div>
);
}

No, you cannot get that metadata in the layout with SSR, at least so far. A layout in the app folder gets passed an object that contains children and params when a dynamic route is used. That's it.
Its role is to wrap all components of a route segment, not only page.js, but also loading.js, not-found.js...
Anything specific to a component should be in it directly. A page title should be in page.js itself. Next.js decided that a project should be structured so.

Related

on NextJS How to prerender shared components of my site?

I have a site built with nextjs with a top navigation bar and a footer. Since those are the same on every page, I didn't add them to their individual js files under pages/, instead, I have a 'wrapper' that adds those to each page. The data of both come from a JSON (2 different languages).
I was trying to use getServerSideProps but that won't work because the navigation bar itself is not inside the page.
What is the 'correct' way of doing this? I cannot hardcode the JSON in the code because it might change and I would prefer not to have to add those sections on the code of every page I have.
I see that the nextjs is preparing a release with the possibility to get static props in _app.js but what is the current solution for this problem?
_app.js
export default function MyApp({ Component, pageProps }) {
return (
<AppWrapper>
<Component {...pageProps} />
</AppWrapper>
);
}
in AppWrapper I create the app context with react hook (createContext) and then wrap it
const WebWrapper = ({ children }) => {
return (
<>
<NavBar />
<main>{children}</main>
<Footer />
</>
);
};

I am trying to create dynamic routes in NextJs. I am using .map to render the page list and am having trouble wiring it together

I am using NextJs and want to create dynamic routes for different projects. I am having trouble getting the list page to render properly. I plan on eventually moving to Postgres but in dev I am using JSON files in the public folder for the time being. I am attempting to use the .map method to render the data from a json file and can not get it running properly. Inside the pages folder I have a project folder which contains an index.js that looks like this.
'''
export default function ProjectList() {
return <h1>Project List</h1>
}
,,,
As well as an [id].js file like this
'''
import { useRouter } from 'next/router'
export default function Project({ project }) {
const router = useRouter()
const { id } = router.query
return (
{project.map(({title, subtitle, image, logo, description}) => (
<div className="Project-Faucets">
<div className="Project-Title">{title}</div>
<div className="Project-Subtext">{subtitle}</div>
<img src={image} alt='visual' className="Project-Image" />
<img src={logo} alt='visual' className="Project-Logo" />
<div className="Project-Content">{description}</div>
</div>
))}
)
}
export async function getServerSideProps({ params }) {
const req = await fetch(`http://localhost:3000/${params.id}.json`);
const data = await req.json();
return {
props: { project: data },
}
};
'''
I have been playing with several different attempts but am having no luck and although I may be wrong I am thinking the .map method may be the cause of my problem. Any help or guidance would be appreciated! After typing this I started thinking it may have to do with my json files not being an array ...... Ill look into that now

What is the best way to get shopOrigin from within Shopify react app?

Overview:
I am using Shopify's CLI to generate an embedded React app. I would like to be able to get the shopOrigin (see code below) from other pages within the application.
Problem:
I have attempted using store-js to store the shopOrigin. This works at first but it seems to clear when I navigate to any other pages within the application.
Note:
Since it is set properly in _app.js I feel that I should simply be able to get the shopOrigin further downstream (i.e. in my index page or any other page) without having to set it in storage.
So, I am completely open to obtaining the shopOrigin a different way without storing it via store-js or any other storage mechanism.
Nonetheless, here is my current code.
Code:
// _app.js
import store from "store-js";
// other imports
class MyApp extends App {
render() {
const { Component, pageProps, shopOrigin } = this.props;
store.set("shopUrl", shopOrigin);
return (
<AppProvider i18n={translations}>
<Provider
config={{
apiKey: API_KEY,
shopOrigin: shopOrigin,
forceRedirect: true,
}}
>
<ClientRouter />
<MyProvider Component={Component} {...pageProps} />
</Provider>
</AppProvider>
);
}
}
// index.js
import store from "store-js";
// other imports
function Index(props) {
console.log("SHOP URL IS HERE: " + store.get("shopUrl"));
}

How to make a single React component then fill it with information based on URL?

I am building a list of people my company is working with. Since it is constantly updating with new people I decided to make a single page web app purely in React as a way to learn it since I am new to it and I wanted to learn it for sometime now.
I have index.js and people.js. In people.js I made an object for every person and they each have own atributes (age, location etc.).
Since I don't know how to do it properly I made for each person Home and Single component of it.
Home component is something like:
export class AnnHome extends Component{
render(){
return(
<div>
<Link to={{pathname: 'influencer/Ann',
component: 'AnnSingle'
}}>
<img
src={require('./img/ann.png')}
alt="{Kofs.name}"
className="Avatar rounded-circle"
/></Link>
<p>{Ann.name}</p>
</div>
)
}
}
Before that component I have defined object 'Ann' with it's info.
My question is:
How to make a one Home component and one Single Component like a template so when I go to /ann to fill SingleComponent with Ann info.
Home Component would be like a list of all clients (people):
https://www.w3schools.com/howto/howto_css_team.asp
something like this.
I currently have a lot of Home components that I've put manually.
Hopefully I described my problem, my english is rusty :D
Hopefully this can help get you started.
As I mentioned above, you want to use the React Router to listen to route changes. Then one specifies which component should be mounted for each route. Here I use the route to match on the individual's name (e.g. so /stace matches the element in people where the person's name attribute is Stace.
import React, { Component } from 'react';
import { BrowserRouter, Route, Switch, Link } from 'react-router-dom';
const people = [
{name: 'Stace', img: 'https://placeimg.com/200/200/people'},
{name: 'Marlo', img: 'https://placeimg.com/201/200/people'},
]
const App = props => (
<BrowserRouter>
<div>
<Switch>
<Route path='/stace' component={Person} />
<Route path='/marlo' component={Person} />
</Switch>
{people.map((p, idx) => <Link to={'/' + p.name}>{p.name}</Link>)}
</div>
</BrowserRouter>
)
const Person = props => {
const name = props['match']['path'].toLowerCase().substring(1);
const person = people.filter(p => p.name.toLowerCase() == name)[0]
return (
<div className='person'>
<img src={person.img || ''}></img>
<h2>{person.name || ''}</h2>
</div>
)
}
export default App;
This is of course only meant to demonstrate the router-related concepts. If you use create-react-app, run yarn add react-router#4.3.0 && yarn add react-router-dom#4.3.1, and replace App.js with the content above, you'll be able to preview the app and explore the ideas expressed above. Clicking on a name will change the route to that name and display the data for that name. Below I've clicked on Stace:

Import or require a bundled js file created by react application

Let's say I have a normal react application using redux and some ajax calls.
If I want to pass it to someone I will give them the bundled js file I created with webpack and ask them to include it in their HTML + render a div with an id of "myApp" for example:
<div id="myApp"></div>
Ok, what if their website is also created with react, and they want to include my bundled js file inside one of their components, and of course render the relevant div?
I tried to use import or require to simulate this:
require('./path/to/myBundle.js');
import './path/to/myBundle.js';
Example:
//...
import './path/to/myBundle.js'; // the file that will render myApp to the relevant div
// ....
export function SomeApp(args){
return(
<div>
<div id="myApp"></div>
<SomeComponent />
</div>
);
};`
This does not work as I get some errors about:
Uncaught Error: Minified React error #37; visit
http://facebook.github.io/react/docs/error-decoder.html?invariant=37
for the full message or use the non-minified dev environment for full
errors and additional helpful warnings.
And when I visit this site I see:
_registerComponent(...): Target container is not a DOM element.
However, if they'll use this file (myBundle.js) outside their components (top level index.html for example) it will work just fine of course.
EDIT:
I forgot to mention that I think I know what the problem is, the application doesn't have the HTML ready with this div yet. but I don't know a good and native way to wait for it to exist.
EDIT #2 following #Frxstrem 's answer:
I'm trying to follow this answer but I think I'm doing it wrong.
I have 2 copies of corry house slingshot demo app as app1 and app2.
changed the 'output' on webpack.config.prod.js of app1 to:
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
filename: 'app1Bundle.js',
library: "App1",
libraryTarget: "umd"
},
I'm trying to render app1 inside the homepage component of app2.
so i copied the "published" files from app1 to the root of app2 and called the folder app1, then added an import call:
import {app1} from '../../app1/app1Bundle';
and a matching tag inside the return function:
const HomePage = () => {
return (
<div>
<app1 />
<h1>App 2</h1>
</div>
);
};
I get the same error as I posted above.
I also tried different combinations:
import app1 from '../../app1/app1Bundle'; // without curly braces
or even just getting the script as a normal js script
import '../../app1/app1Bundle';
or
require('../../app1/app1Bundle');
and then tried to render a normal div tag with an id of "app1"
const HomePage = () => {
return (
<div>
<div id="app1"></div>
<h1>App 2</h1>
</div>
);
};
nothing seems to work as I still get the same error.
I think the problem is the timing of the script load and the rendering of the elements. I think the div does not exist yet when the bundled script is searching for it.
By default, Webpack will expose the entry module as a variable, which is useful when you include scripts with a <script> tag. (Because of this, if you require it you would likely just get {}.) However, if you want to load your bundle from other modules, you'll need to tell Webpack to expose it as an exported module instead.
The easiest way to do this is to set
{
...
"libraryTarget": "umd"
}
in your Webpack configuration. With that, Webpack knows that it should expose your entry module as a module that can be required in Webpack, but can also be loaded with a <script> tag as necessary.
Webpack libraryTarget documentation
The main problem i faced was to include the bundled js file of app1 after the DOM contains the target div it needs.
What i ended up doing was, creating a component in app2 project that will require() the bundled js file on componentDidMount() and will render and return the target div with a relevant id.
The reason i created a component is purely for re-usability purpose, instead of requiring this script with componentDidMount() on every component that needs it.
So, this is the component:
import React from 'react';
class AppOne extends React.Component {
componentDidMount() {
require('../app1/app1Bundle.js');
}
render() {
return (
<div id="app1"></div>
);
}
}
export default AppOne;
And this is how i use it in other component:
import React from 'react';
import AppOne from './AppOne';
const HomePage = () => {
return (
<div>
<h1>App 2 - wrapper for app1</h1>
<hr />
<AppOne />
<hr />
<h1>This is App2 as well </h1>
</div>
);
};
export default HomePage;
It's working fine. my only concern is that i may face some conflicts with react because i'm using 2 react apps though for ow i don't see any errors.
I guess that's an issue for a different question.
EDIT:
If someone will use this approach you should note that this will work only for the first load. because after the component will re-render itself the bundled script will not run again.

Categories