開源日報每天推薦一個 GitHub 優質開源項目和一篇精選英文科技或編程文章原文,堅持閱讀《開源日報》,保持每日學習的好習慣。

2024年1月31日,開源日報第1091期:
今日推薦開源項目:《Next.js》
今日推薦英文原文:《 What is the template file, and how can you use it in nextjs?》


開源項目

今日推薦開源項目:《Next.js》傳送門:項目鏈接

推薦理由: React 流行框架,被世界上一些最大的公司使用,Next.js讓你能夠使用React組件的強大功能創建高質量的Web應用程序

網站直達:nextjs.org


英文原文

今日推薦英文原文:What is the template file, and how can you use it in nextjs?

推薦理由: 主要講在Next.js中使用模板文件(template file)的概念以及與布局文件(layout file)的一些區別,還比較了模板文件和布局文件在接受props方面的不同,以及它們在實際應用中的區別


What is the template file, and how can you use it in nextjs?

To understand the concept behind template vs layout files with an example.

Template files work similarly to Layout files in nextjs; the template.tsx file wraps children's components.

// output

<Layout>
  {/* Note that the template is given a unique key. */}
  <Template key={routeParam}>{children}</Template>
</Layout>

But one big difference between a template.tsx and a layout.tsx file is that Layout persists across routes and maintains state.

On the other hand, the template file creates a new instance for each child on navigation.

The simple word is that the layout component renders one-time in dom. But in the Template file, it is changed on every render and generates a new instance of each render.

All the code is available on GitHub and you can run code directly with code sandbox. Visit the post route in the demo and see the difference between the layout and template.

How to use a template file?

To use a template file, first, you need to create an template.tsx or template.jsxfile in your app folder.

// src/app/template.jsx

export default function Template({ children }: { children: React.ReactNode }) {
  return <div>{children}</div>
}

You can convert your template file into client and server components. With the client component, you can use any react hook, for example, the useState and useEffect hooks in the template file.

// src/app/template.jsx

'use client'

import { useEffect, useState } from "react";

export default function Template({ children }: { children: React.ReactNode }) {

  // useState Hook
  const [count, setCount] = useState<number>(0)

  // useEffect Hook
  useEffect( () => {console.log(" log here. ")}, [])

  return (
      <main className="py-12 container m-auto px-6 text-gray-600 md:px-12 xl:px-6">

        <h1> count: {count}</h1>

        <button onClick={() => { setCount(()=>{return count + 1}) }} className="text-gray-800 transition-colors duration-300 transform dark:text-gray-200 border-b-2 border-blue-500 mx-1.5 sm:mx-6">
          {count}
        </button>

        <div className="lg:w-3/4 xl:w-2/4 mx-auto"> {children} </div>

      </main>
  )

}

Some developers argue you can also use React Hook with layout files and convert layout files into client components.

// src/app/layout.jsx

'use client'

import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css';
import { Header } from '@/components/Header';

import { useEffect, useState } from "react";
const inter = Inter({ subsets: ['latin'] })

// export const metadata: Metadata = {
//   title: 'Layout and Template',
//   description: 'Understand the layout and template.'
// }

export default function RootLayout({ children }: { children: React.ReactNode }) {
  // useState Hook
  const [count, setCount] = useState<number>(0)

  // useEffect Hook
  useEffect(() => { console.log(" log here. ") }, [])

  return (
    <html lang="en">
      <body className={inter.className}>

        <h1> count: {count}</h1>

        <button onClick={() => { setCount(() => { return count + 1 }) }} className="text-gray-800 transition-colors duration-300 transform dark:text-gray-200 border-b-2 border-blue-500 mx-1.5 sm:mx-6">
          {count}
        </button>

        <Header />
        {children}
      </body>
    </html>
  )
}

The counter works fine in the browser when you convert the layout file into a client component.

it work file

However, you cannot use metadata when you convert the layout file into a client component. You see the following error.

You are attempting to export 「metadata」 from a component marked with 「use client」, which is disallowed. Either remove the export, or the 「use client」 directive.

You are attempting to export "metadata" from a component marked with "use client," which is disallowed. Either remove the export or the "use client" directive.

What is the difference between layout and template files in nextjs?

The core difference between Layout and template file. The layout file is preset across routes and maintains state.

To understand the meaning of "preset across routes and maintain state." We can understand the difference between layout and template files in real-life examples. First, watch the following gif.

You are attempting to export 「metadata」 from a component marked with 「use client」, which is disallowed. Either remove the export, or the 「use client」 directive.

understand the meaning of preset across routes and maintain state

In the gif, you see two navbars; the first navbar has a logo, home, about, and contact item, which is part of the layout.tsx file.

The second navbar has javascript, typescript, Reactjs, and Nextjs items; it is part of the template.tsx file.

// src/app/template.jsx

'use client'

import { motion } from "framer-motion"
import Link from "next/link";
import { useEffect } from "react";

export default function Template({ children }: { children: React.ReactNode; }) {

  useEffect(...)

  return (

    <>

      <nav className="relative bg-white shadow dark:bg-gray-800">
        <div className="container px-6 pt-3 mx-auto">
          <div className="py-3 mt-3 -mx-3 overflow-y-auto whitespace-nowrap scroll-hidden">
            <nav className="container flex items-center justify-left px-6 mx-auto ">

              {// Animate with framer motion }
              <motion.li initial={{ opacity: 0, x: -20 }} animate={{ opacity: 1, x: 0 }} transition={{ delay: 0.1 }} className="list-none mx-4 text-sm leading-5 text-gray-700 transition-colors duration-300 transform dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400 hover:underline md:my-0">
                <Link href="/javascript" >JavaScript</Link>
              </motion.li>

              <motion.li initial={{ opacity: 0, x: -20 }} animate={{ opacity: 1, x: 0 }} transition={{ delay: 0.1 }} className="list-none mx-4 text-sm leading-5 text-gray-700 transition-colors duration-300 transform dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400 hover:underline md:my-0">
                <Link href="/typescript" >TypeScrip</Link>
              </motion.li>

              <motion.li initial={{ opacity: 0, x: -20 }} animate={{ opacity: 1, x: 0 }} transition={{ delay: 0.1 }} className="list-none border-b-2 border-transparent hover:text-gray-800 transition-colors duration-300 transform dark:hover:text-gray-200 hover:border-blue-500 mx-1.5 sm:mx-6">
                <Link href="/reactjs" >Reactjs</Link>
              </motion.li>

              <motion.li initial={{ opacity: 0, x: -20 }} animate={{ opacity: 1, x: 0 }} transition={{ delay: 0.1 }} className="list-none border-b-2 border-transparent hover:text-gray-800 transition-colors duration-300 transform dark:hover:text-gray-200 hover:border-blue-500 mx-1.5 sm:mx-6">
                <Link href="/nextjs" >Nextjs</Link>
              </motion.li>

            </nav>
          </div>
        </div>
      </nav>

      <main className="py-12 container m-auto px-6 text-gray-600 md:px-12 xl:px-6">
        {children}
      </main>

    </>
  )

}

Both navbar items are animated with framer motion. You see the first navbar animate only once. After that, it does not animate when we move one route to the other hand.

The second navbar item animates on every render when we move on javascript to the typescript route.

The first navbar comes with preset across routes and maintains state, and the second navbar generates a new instance on every render. We see the animation on every navigation in the second navbar.

Props in template vs. layout file.

The template only accepts the children's props as an argument. On the other hand, the layout file accepts both children and params.

// accept props 

export default function Template({ children }: { children: React.ReactNode }) {}
// vs 
export default function ShopLayout({ children, params}: {children: React.ReactNode; params: {tag: string; item: string}}) {}

If you try to access parms and searchParams argument in the template, you receive an undefined.

// src/app/[tech]/template.tsx

export default function Template({ children, params, searchParams }: {
  children: React.ReactNode
  params: { slug: string }
  searchParams: { [key: string]: string | string[] | undefined }
}) {}

Conclusion

The layout file is more common than the template file in nextjs; both files have different use cases.

The layout file renders only one in dom; conversely, the template renders on every state change or navigation( go one route to another).


下載開源日報APP:https://openingsource.org/2579/
加入我們:https://openingsource.org/about/join/
關注我們:https://openingsource.org/about/love/