I am getting undefined in console log instead of data
My Index.js (pages file)
import Head from "next/head";
import Link from "next/link";
import axios from "axios";
import Test from "../components/Test";
import styles from "../styles/Home.module.css";
function Home() {
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<Test />
</div>
);
}
export default Home;
My Test.js (components file) from which i get error
import React from "react";
import axios from "axios";
function Test({ data }) {
console.log(data);
return <div>Check console log log</div>;
}
export async function getStaticProps() {
const { data } = await axios.get(
`https://jsonplaceholder.typicode.com/todos/1`
);
return {
props: {
data,
},
};
}
export default Test;
Console Output
undefined
Please help me i dont know why axios, fetch doesnt work components but working in pages/index.js
According to Next.js docs getStaticProps method works on Page Components but not in child components. You called in Test which is child component.
What You can do fetch data in your page component than pass the data via props & then you can access the data from child component If you are trying to use getStaticProps.
Example:
Index.js
import Head from "next/head";
import Link from "next/link";
import axios from "axios";
import Test from "../components/Test";
import styles from "../styles/Home.module.css";
function Home({ data }) {
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<Test data={data} />
</div>
);
}
export async function getStaticProps() {
const { data } = await axios.get(
`https://jsonplaceholder.typicode.com/todos/1`
);
return {
props: {
data,
},
};
}
export default Home;
Test Component:
import React from "react";
function Test({ data }) {
console.log(data);
return <div>Check console log log</div>;
}
export default Test;
Without getStaticProps & data directly in child component:
Index.js:(Page)
import Head from "next/head";
import Link from "next/link";
import Test from "../components/Test";
import styles from "../styles/Home.module.css";
function Home() {
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<Test />
</div>
);
}
export default Home;
Test.js(Component):
import React from "react";
import useSWR from "swr";
import fetch from "isomorphic-unfetch";
const fetcher = (url) => fetch(url).then((r) => r.json());
function Test() {
const { data } = useSWR('yourURL', fetcher);
if (error) return <div>failed to load</div>
if (!data) return <div>loading...</div>
return <div>hello {data.name}!</div>
}
export default Test;
Solved! data passed from pages/index.js to components/Test.js
I found that "getStaticProps" doent supported in components only in pages dont know why it doesnt supported
Related
I was setting up redux in my Next JS project but the useDispatch hook wasn't working inside my Nav component. The nav was wrapped in the tag provided by Next JS. But when I moved my Nav component outside the tag, the useDispatch hook started working. Why is my app behaving like this?
Error:
_app.js:
import "../styles/globals.css";
import { useStore } from "../app/store/store";
import { Provider } from "react-redux"; 2
export default function App({ Component, pageProps }) {
const store = useStore(pageProps.initialReduxState);
return (
<Provider store="{store}">
<Component {...pageProps} />
</Provider>
)
}
index.js:
import styles from "../styles/Home.module.css";
import Head from "next/head";
import { useEffect } from "react";
import { useDispatch } from "react-redux";
import { authCheckState } from "../app/store/actions/auth";
import Nav from "../app/components/Nav";
import Footer from "../app/components/Footer";
import MainCarousel from "../app/components/MainCarousel";
import BestSellers from "../app/components/BestSellers";
export default function Home() {
const dispatch = useDispatch();
useEffect(() => {
dispatch(authCheckState());
});
return (
<>
<Head>
<Nav />
</Head>
<main className={styles.main}>
<MainCarousel />
<BestSellers />
</main>
<Footer />
</>
);
}
I'm having an issue displaying data pulled in (from Prismic) with getInitialProps in the _app.js file. I have followed the Prismic slice machine tutorial, which includes defining a header and navigation in Prismic and displaying that data on the frontend - that all works fine.
I've defined a footer now, have included the call for the footer data in the same place and way I have for the header data in the _app.js file, but that data does not display on the frontend. The error message I am seeing is:
TypeError: Cannot read properties of undefined (reading 'data')
and references the call stack to my _document.js file, but I cannot understand where the issue is in that file.
_document.js:
import Document, { Html, Head, Main, NextScript } from 'next/document'
import { repoName } from '../prismicConfiguration'
import Link from 'next/link'
export default class extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html lang="en">
<Head>
<script async defer src={`//static.cdn.prismic.io/prismic.js?repo=${repoName}&new=true`} />
</Head>
<body className="">
<Main />
<NextScript />
</body>
</Html>
)
}
}
_app.js:
import React from 'react'
import NextApp from 'next/app'
import { Client } from '../utils/prismicHelpers'
import '../styles/style.scss'
export default class MyApp extends NextApp {
static async getInitialProps(appCtx) {
const menu = await Client().getSingle('menu') || null
const footer = await Client().getSingle('footer') || null
console.log("FOOTER", footer)
console.log("MENU",menu)
return {
props: {
menu: menu,
footer: footer
},
}
}
render() {
const { Component, pageProps, props } = this.props
return (
<Component {...pageProps} menu={props.menu} footer={props.footer} />
)
}
}
Footer.js (where the data should be getting displayed):
import React from 'react'
import Link from 'next/link'
// Project functions
import { linkResolver } from '../prismicConfiguration'
import { RichText } from 'prismic-reactjs'
// Footer component
export default function Footer({ footer }) {
return (
<footer className="footer">
<div className="container max-width-lg">
{footer.data.eyebrow}
<RichText render={footer.data.headline} />
<RichText render={footer.data.description} />
<Link href={linkResolver(footer.data.link)}>
<a>
{footer.data.linkLabel}
</a>
</Link>
{ footer.data.copyrightText }
{ footer.data.signoffText }
</div>
</footer>
)
}
I'm so confused as to why this footer data cannot be displayed or read when it has been defined and called in the same way as the header, which works completely fine. I am console logging both sets of data in the _app.js file and both are returning fine in the terminal, so I am confident what I am adding to the footer component file is correct. For some context, the reason I am pulling this data in the _app.js file is that its data that needs to be across all pages rather than calling it on every single page.
Where am I going wrong here?
Thanks
Addition:
The Footer component is being added to a layout component:
import React from 'react'
import { useEffect } from 'react'
import Head from 'next/head'
import { useRouter } from 'next/router'
import Script from 'next/Script'
// Components
import Header from './Header'
import Footer from './Footer'
// Project functions
import * as gtag from '../lib/gtag'
// Layout component
const Layout = ({ children, footer, menu }) => {
const router = useRouter()
useEffect(() => {
const handleRouteChange = (url) => {
gtag.pageview(url)
}
router.events.on('routeChangeComplete', handleRouteChange)
return () => {
router.events.off('routeChangeComplete', handleRouteChange)
}
}, [router.events])
return (
<div className="page_wrapper">
<Head>
{/* <meta charSet="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon/favicon.ico" />
<script dangerouslySetInnerHTML={{ __html: `document.getElementsByTagName("html")[0].className += " js";`}}
/> */}
{/* <Link
rel="preload"
href="/fonts/font-file.woff2"
as="font"
crossOrigin=""
/> */}
</Head>
<Script
id="codyhouse-utils-js"
src="https://unpkg.com/codyhouse-framework/main/assets/js/util.js"
strategy="beforeInteractive"
/>
<Script
strategy="afterInteractive"
src={`https://www.googletagmanager.com/gtag/js?id=${gtag.GA_TRACKING_ID}`}
/>
<Script
id="gtag-init"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${gtag.GA_TRACKING_ID}', {
page_path: window.location.pathname,
});
`,
}}
/>
{/* <Script
src="https://www.google.com/recaptcha/api.js?&render=explicit"
strategy="afterInteractive"
/> */}
<Header menu={menu} />
<main>
{children}
</main>
<Footer footer={footer} />
</div>
);
};
export default Layout
I've created a SSR React app that loads data on the server and sends it to the client as html. The problem kicks in after the initial server request has been served and i try to switch to a different navigation link. The url changes to the correct path but the page itself breaks with a TypeError: Cannot read property 'length' or 'map' of undefined. I believe the fetching somehow is not working on the client side, because if i turn off JavaScript from the browser everything works just fine.
The App has four routes, Home, Movies (needs to fetch data), TvShows (needs to fetch data) and PageNotFound. Again, the problem occurs when for example I open the Home page and try to switch to Movies. However, if i open Movies or TvShows first everything loads correctly because of the initial request being served by the server. Here is my file structure:
And here's the content of some of my files:
index.js (Server.js)
import "#babel/polyfill";
import express from "express";
import { applyMiddleware, createStore } from "redux";
import Routes from "./client/Routes";
import { matchRoutes } from "react-router-config";
import renderer from "./helpers/renderer.js";
import thunk from "redux-thunk";
import reducers from "./reducers";
const compression = require("compression");
const app = express();
app.use(compression());
app.use(express.static("public")); //treats the public(client side) directory as public, available to the outside world
// This is fired every time the server side receives a request
app.get("*", (req, res) => {
// Create a new Redux store instance
const store = createStore(reducers, {}, applyMiddleware(thunk));
const promises = matchRoutes(Routes, req.path)
.map(({ route }) => {
return route.loadData ? route.loadData(store) : null;
})
.map((promise) => {
if (promise) {
return new Promise((resolve, reject) => {
promise.then(resolve).catch(resolve);
});
}
});
Promise.all(promises).then(() => {
// Send the rendered page back to the client
// Grab the initial state from our Redux store
const context = {};
//const finalState = store.getState();
const content = renderer(req, store, context);
if (context.notFound) {
res.status(404);
}
res.send(content);
});
});
const PORT = process.env.PORT || 3000;
app.listen(PORT);
client.js
import "#babel/polyfill";
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { Provider } from "react-redux";
import { renderRoutes } from "react-router-config";
import Routes from "./Routes";
import reducers from "../reducers";
// Grab the state from a global variable injected into the server-generated HTML
const preloadedState = window.__PRELOADED_STATE__;
// Create Redux store with initial state
const store = createStore(reducers, preloadedState, applyMiddleware(thunk));
ReactDOM.hydrate(
<Provider store={store}>
<BrowserRouter>
<div>{renderRoutes(Routes)}</div>
</BrowserRouter>
</Provider>,
document.querySelector("#root")
);
renderer.js
import React from "react";
import serialize from "serialize-javascript";
import { renderToString } from "react-dom/server";
import { StaticRouter } from "react-router-dom";
import { Provider } from "react-redux";
import Routes from "../client/Routes";
import { renderRoutes } from "react-router-config";
import { Helmet } from "react-helmet";
// Render the component to a string
export default (req, store, context) => {
const html = renderToString(
<Provider store={store}>
<StaticRouter location={req.path} context={context}>
<div>{renderRoutes(Routes)}</div>
</StaticRouter>
</Provider>
);
const helmet = Helmet.renderStatic();
return `
<!doctype html>
<html>
<head>
${helmet.title.toString()}
${helmet.meta.toString()}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
</head>
<body>
<div id="root">${html}</div>
<script>
// WARNING: See the following for security issues around embedding JSON in HTML:
// https://redux.js.org/recipes/server-rendering/#security-considerations
window.__PRELOADED_STATE__ = ${serialize(store.getState())}
</script>
<script src="/bundle.js"></script>
</body>
</html>
`;
};
Routes.js
import App from "./App";
import HomePage from "./pages/HomePage";
import MovieListPage from "./pages/MovieListPage";
import TvShowsPage from "./pages/TvShowsPage";
import NotFoundPage from "./pages/NotFoundPage";
//using spread operator for the components
//and loadData function(if available)
//because they are imported in object form now
export default [
{
...App, //no path added to App, meaning it will always be displayed on screen
routes: [
{
...HomePage,
path: "/",
exact: true,
},
{
...MovieListPage,
path: "/movies",
exact: true,
},
{
...TvShowsPage,
path: "/tvshows",
exact: true,
},
{
...NotFoundPage, //will be shown if react router can't match any of the defined routes
},
],
},
];
MovieListPage.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchMovies } from "../../actions";
import { Helmet } from "react-helmet";
class MovieListPage extends Component {
// Have state ready for both Movies and TvShows link clicks/direct requests
componentDidMount() {
if (this.props.movies) return;
this.props.fetchMovies();
}
renderData() {
return this.props.movies.results.map((movie) => {
return (
<div
key={movie.id}
className="card text-center m-3"
style={{ width: "15rem" }}
>
<img
className="card-img-top"
alt="..."
src={this.dynamicUrl(movie)}
/>
<div className="card-body">
<h5 className="card-title">{movie.title}</h5>
<p className="card-text font-weight-light">{movie.release_date}</p>
<a href={this.dynamicLink(movie)} className="btn btn-secondary">
TMDB
</a>
</div>
</div>
);
});
}
dynamicUrl(movie) {
let url = "https://image.tmdb.org/t/p/w200/" + movie.poster_path;
return url;
}
dynamicLink(movie) {
let link = "https://www.themoviedb.org/movie/" + movie.id;
return link;
}
head() {
return (
<Helmet>
<title>{`${this.props.movies.results.length} Movies Loaded`}</title>
<meta property="og:title" content="Movies" />
</Helmet>
);
}
render() {
return (
<div className="container">
{this.head()}
<div className="row">{this.renderData()}</div>
</div>
);
}
}
function mapStateToProps(state) {
return { movies: state.movies };
}
function loadData(store) {
return store.dispatch(fetchMovies());
}
//exporting the component and the loadData function (if present)
//in the form of an object(key:value pair)
// to avoid overlap of different loadData function imports in Routes
export default {
loadData,
component: connect(mapStateToProps, { fetchMovies })(MovieListPage),
};
I can't seem to figure out what is it that i'm missing.
I found the issue in renderer.js. I didn't provide the correct path for my client-side bundle.js in the html served by the server. Instead of <script src="/bundle.js"></script> it had to be <script src="/public/bundle.js"></script>.
As you can see in the below next.js code I am trying to defer load the render blocking css by giving my main.css file path in href attribute but I am struggling to do it in next.js. What I want is after loading the critical css in _document.js tag under tag, to load the non-critical css which is not above the fold.
_app.js
import App from "next/app"
import Head from "next/head"
import React from "react"
import { observer, Provider } from 'mobx-react'
import Layout from "../components/Layout"
import allStores from '../store'
export default class MyApp extends App {
componentDidMount = () => {
};
render() {
const { Component, pageProps, header, footer, } = this.props
return (
<>
<Head >
<link rel="preload" href="path/to/main.css" as="style"
onLoad="this.onload=null;this.rel='stylesheet'"></link>
</Head>
<Provider {...allStores}>
<Layout header={header} footer={footer}>
<Component {...pageProps} />
</Layout>
</Provider>
</>
)
}
}
as #chrishrtmn said at _document.js you can do like this:
import Document, { Main, NextScript } from 'next/document';
import { CriticalCss } from '../components/CriticalCss';
class NextDocument extends Document {
render() {
return (
<html>
<CriticalCssHead />
<body>
<Main />
<NextScript />
</body>
</html>
);
}
}
export default NextDocument;
as in your component you can put the CSS:
import { readFileSync } from 'fs';
import { join } from 'path';
export interface Props {
assetPrefix?: string;
file: string;
nonce?: string;
}
export const InlineStyle: React.FC<Props> = ({ assetPrefix, file, nonce }) => {
const cssPath = join(process.cwd(), '.next', file);
const cssSource = readFileSync(cssPath, 'utf-8');
const html = { __html: cssSource };
const id = `${assetPrefix}/_next/${file}`;
return <style dangerouslySetInnerHTML={html} data-href={id} nonce={nonce} />;
};
I got the source for this code from the current repo:
https://github.com/JamieMason/nextjs-typescript-tailwind-critical-css
have a look here
https://github.com/JamieMason/nextjs-typescript-tailwind-critical-css/tree/master/components/CriticalCssHead
Here's my current favorite solution, sourced from here:
https://github.com/facebook/react/issues/12014#issuecomment-434534770
It results in two empty <script></script> tags in your head, but works.
<script
dangerouslySetInnerHTML={{
__html: `</script><link rel='preload' href='style.css' as='style' onload="this.onload=null;this.rel='stylesheet'"/><script>`,
}}
/>
The Next.js team indicated that a similar strategy is possible with their component, but in practice, I was getting compilation errors:
https://github.com/vercel/next.js/issues/8478#issuecomment-524332188
The error I received was:
Error: Can only set one of children or props.dangerouslySetInnerHTML.
Might have to move the <Head> into _document.js instead of _app.js according to the documentation.
I'm using NextJs in a project and and I created a component where I load dynamic data, if I load via localhost:3000/faq, it works normally, but if I try to import that same component into index.js, an error occurs. I probably need to pass props, but I don't know how to do that.
This is my faq.js
import React from 'react'
import fetch from 'isomorphic-unfetch'
function Faq({ data }) {
return (
<div>
<ul>
{data.map((item) => (
<li key={item.id}>{item.question}{item.answer}</li>
))}
</ul>
</div>
)
}
export async function getStaticProps() {
const res = await fetch('./data/faq.json')
const data = await res.json()
return {
props: {
data,
},
}
}
export default Faq
Here is the index.js
import Layout from '../components/layouts/layout'
import Faq from './faq'
import React, {Component} from 'react'
export default class App extends Component {
render() {
return(
<Layout>
<h1>I am Home Page</h1>
<Faq />
</Layout>
)
}
}
Does anyone know how to load faq.js into index.js?
Inside index.js you've import ./faq with name About but inside the render function you used it as <Faq />. Should be in this way:
import Layout from '../components/layouts/layout'
import Faq from './faq'
import React, {Component} from 'react'
export default class App extends Component {
render() {
return(
<Layout>
<h1>I am Home Page</h1>
<Faq />
</Layout>
)
}
}