开源日报每天推荐一个 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/