No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Curso de Next.js

Curso de Next.js

Jonathan Alvarez

Jonathan Alvarez

Extendiendo el App

13/27
Recursos

Aportes 25

Preguntas 5

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

o inicia sesión.

En el archivo _app podemos inyectarle anábolicos (providers, themes, props, o cualquier cosa que necesitemos) a toda nuestra aplicación.

Todos los cambios en _app y _document, requieren reinicio de servidor.

En react 18 children fue eliminado del tipo FC, para que typescript no de error debes de añadirlo por cuenta propia

import React from 'react'
import { Navbar } from '@components/Navbar/Navbar'

type Props = {
  children: React.ReactNode;
};

export const Layout: React.FC<Props> = ({ children }) => {
  return (
    <>
      <Navbar />
      {children}
      <footer>
        <p>This is a footer</p>
      </footer>
    </>
  )
}

Solución de Stack Overflow

Documentación de Custom App:
https://nextjs.org/docs/advanced-features/custom-app

Casos de uso:

  • Providers (Context)
  • Themes
  • Layout
  • Props adicionales

Y Que pasa si queremos tener dos layouts ejemplo la pagina login esa no tiene ni navbar ni footer como hacemos para tener dos o tres layouts ¿?

Esto hice para mostrar la página 404 sin el lay out.

import React from 'react';
import { useRouter } from 'next/router';
import Layout from '../src/components/Layout';
import AppContext from '../src/context/AppContext';
import useInitialState from '../src/hooks/useInitialState'

function MyApp({ Component, pageProps }) {
  const router = useRouter();
  const initialState = useInitialState();
  return <AppContext.Provider value={initialState}><CustomLayout pathname={router.pathname}><Component {...pageProps} /></CustomLayout></AppContext.Provider>
}

const CustomLayout = ({ pathname, children }) => (
  <>
    {
      pathname === "/_error" ? <>{children}</>
        : (
          <Layout>{children}</Layout>
        )
    }
  </>
)
// Only uncomment this method if you have blocking data requirements for
// every single page in your application. This disables the ability to
// perform automatic static optimization, causing every page in your app to
// be server-side rendered.
//
// MyApp.getInitialProps = async (appContext) => {
//   // calls page's `getInitialProps` and fills `appProps.pageProps`
//   const appProps = await App.getInitialProps(appContext);
//
//   return { ...appProps }
// }

export default MyApp

🌟 Extendiendo el app de Next.js

<h4>Ideas/conceptos claves</h4>

documentación de Next.js para un custom app

Advanced Features: Custom App | Next.js

<h4>Apuntes</h4>

La forma en la que Next.js renderiza en el navegador los contenidos es en el siguiente orden:

  • Document
    • App
      • Nuestra Aplicación

para usar el app de nuestra aplicación lo que haremos sera utilizar el template de la documentación de Next.js para un custom app, el cual debemos crear un archivo llamado “_app.js” en pages

import { AppProps } from 'next/app'

function MyApp({ Component, pageProps }: AppProps) {
  // Providers - Context/Providers, Theme, data
  // Layout
  // props adicionales
  return <Component {...pageProps} />
}

export default MyApp

RESUMEN: Para crear contenedores de nuestra aplicaciones, que deseemos aplicarlo a nivel global, es muy util usar el app de Next.js creando el archivo _app.js

Una opcion alternativa para los nombramientos de los componentes es ponerle el nombre a la carpeta y al archivo tsx llamarlo index . Es decir :

Layout >
index.tsx

Esto con el fin de que en el import se haga referencia a una ruta mas corta e igual de diciente que la que coloca Jonathan, asi:

import Layout from '../components/Layout
	
en vez de 

import Layout from '../components/Layout/Layout';

Extendiendo el App

NextJS primero renderiza el document, luego el app y ese app es el que engloba nuestra aplicación.

Cuando hacemos cambios en app o en document debemos reiniciar el servidor.

Creamos un componente Layout:

import React from 'react'
import Navbar from 'components/Navbar/Navbar'

const Layout: React.FC = ({ children }) => {
  return (
    <div>
      <Navbar />
      {children}
      <footer>This is the footer</footer>
    </div>
  )
}

export default Layout

Y lo usamos en ‘pages/_app.tsx’:

import type { AppProps } from 'next/app'
import Layout from '../components/Layout/Layout'

export default function MyApp({ Component, pageProps }: AppProps): JSX.Element {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  )
}

Segun la documentacion para typescript de custom app en nextjs
en este momento, inicialmente el archivo app.tsx viene quedando

import type { AppProps } from 'next/app'

export default function MyApp({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />
}

A mi bo me funciono el React.FC, en este articulo explican porque no es buena práctica (Leí por ahí que su uso fue descontinuado) y que podemos usar en su lugar.

Cuando hacemos una aplicación con React, el archivo que contiene toda nuestra aplicación es el App.js, sin embargo, con NextJS no hace falta que lo creemos porque este ya viene por debajo con el. Pero qué pasa si queremos extender o cambiar la funcionalidad que viene por defecto?

Bueno en ese caso como vimos la extensión del document, también podemos extender lo que viene por defecto en el App.
Para eso, como lo hicimos con document creando el archivo document_.tsx || _document.jsx, debemos crear un archivo debajo de pages llamado _pages.jsx o .tsx.

Por qué es útil extender el app por defecto de next?

  • Podemos crear Providers (Context, Redux)
  • Crear el provider del theme de la aplicación
  • Pasar datos desde un archivo a cualquier otro
  • Crear layouts para toda nuestra aplicación
  • Pasar props adicionales

En el archivo _app que vamos a crear, debemos utilizar un template que la documentación oficial de NextJS nos proporciona

    import { AppProps } from 'next/app'
    function MyApp({ Component, pageProps }: AppProps) {
        return <Component {...pageProps} />
    }
    export default MyApp

En el caso de que lo usemos par crear un layout debe ser de la siguiente manera (suponiendo que el layout es el mismo para todas las páginas):
Layout.tsx:

import React from 'react';

    const Layout: React.FC = ({children}) => {
        <>
            <nav>
                <ul>
                    <li>Home</li>
                    <li>About</li>
                    <li>Contact</li>
                </ul>
            </nav>
            {children}
            <footer>
                All rights reserved
            </footer>
        </>
    }

    export default HomePage

App.tsx:

import { AppProps } from 'next/app'
    import Layout from './Layout'

    function MyApp({ Component, pageProps }: AppProps) {

        return (
            <Layout>
                <Component {...pageProps} />
            </Layout>
        )
    }

    export default MyApp

Qué pasa si para ciertas páginas deseemos usar un layout diferente? Como por ej, signup, login, splash screen, etc.
Para eso, tenemos que modificar el comportamiento de nuestro archivo _app por lo siguiente:

 import DefaultLayout from "../layouts/default"

    export default function MyApp({ Component, pageProps }) {
    // Use the layout defined at the page level, if available

    const getLayout = Component.getLayout || ((page) => <DefaultLayout>{page}</DefaultLayout>)

    return getLayout(<Component {...pageProps} />)
    }

Aquí estamos diciendo que, si en el componente que se esta renderizando no hay un método ‘getLayout’, nos renderice un layout por defecto. Sin embargo, podemos usar un layout diferente con el método getLayout en la página que queramos, ahora que modificamos el _app:

Login.tsx

        import React from 'react'
        import InitialLayout from '../layouts/initial'

        function Login() {
            return (
                <div>Login</div>
            )
        }

        Login.getLayout = (page) => {
            return (
                <InitialLayout>{page}</InitialLayout>
            )
        }

        export default Login;

El Login page, utiliza un método getLayout, que recibe el contenido de este, para que funcione debemos retornar un JSX, del layout deseado conteniendo al contenido de la página, que es el parámetro que recibe esta función.

RESUMEN: Para crear contenido global de nuestra aplicación, es recomendable extender el app de next

Jon! te fuiste un nivel .mas arriba del necesario para importar en NavBar

se podria hacer, ../Navbar/Navbar directamente.

nada una boludez

import React, { ReactNode } from 'react';
import NavBar from '../Navbar/Navbar';

interface Props {
    children: ReactNode;
}

const Layout = ({children}: Props) => {
    return(
        <div>
            <NavBar />
            {children}
            <footer>soy el pie de pagina</footer>
        </div>
    );
}

export default Layout;```

tambien lo podemos hacer de la siguiente forma :D

Si estás usando TypeScript este es el código que pegas en el _app.tsx:

import type { AppProps } from 'next/app'

export default function MyApp({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />
}

En el archivo _app.js yo he inyectado React Conext Providers dependiendo de la ruta. Aun ando aprendiendo del todo Next.js pero ha sido muy uutil esto de extender la funcionalidad de los componentes

Aca les dejo la documentación oficial para los que lo estén haciendo en las últimas versiones.

https://nextjs.org/docs/getting-started/installation#creating-directories

Si les marca error el children, solo tienen que tiparlo mediante una interface:

interface Props {
  title: string;
  children: JSX.Element
}

Después solo extienden el FC:

const Layout: FC<Props> = ({ children, title }) => {}

De forma explicita (sin usar React.FC) código en el layout:

import React from "react";
import { Navbar } from '../Navbar';
type Props = {
  children?: JSX.Element;
};
function Layout({children}: Props) {
  return (
    <div>
      <Navbar />
      {children}
      <footer>This is the footer</footer>
    </div>
  )
}
export { Layout }

Para quien tenga el error ‘‘Component’ cannot be used as a JSX component’, la solución es actualizar las dependencias de tipos de React a la versión 18.0.6.

el document en Typescript ahora es asi:

import Document, { DocumentContext, DocumentInitialProps } from 'next/document'

class MyDocument extends Document {
  static async getInitialProps(
    ctx: DocumentContext
  ): Promise<DocumentInitialProps> {
    const initialProps = await Document.getInitialProps(ctx)

    return initialProps
  }
}

export default MyDocument

Actualmente la documentacion recomienda para typescrip el siguiente codigo

https://nextjs.org/docs/basic-features/typescript#custom-app

import type { AppProps } from 'next/app'

export default function MyApp({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />
}

Dejo este articulo con ejemplos interesantes
https://jools.dev/nextjs-_appjs-example

Todo lo que pasen por las props del tag component queda disponible en las props de todos los hijos (es decir, de todos los componentes de la app:

return(
          <Component {...pageProps} globalProp={algo} otraGlobalProp={otroAlgo} />
    )

_app.tsx

// import App from 'next/app'

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

// Only uncomment this method if you have blocking data requirements for
// every single page in your application. This disables the ability to
// perform automatic static optimization, causing every page in your app to
// be server-side rendered.
//
// MyApp.getInitialProps = async (appContext) => {
//   // calls page's `getInitialProps` and fills `appProps.pageProps`
//   const appProps = await App.getInitialProps(appContext);
//
//   return { ...appProps }
// }

export default MyApp