You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js中如何向Layout组件传递Props实现待办项筛选

Next.js 单页面布局(Per-Page Layouts)向Layout传递回调的解决方案

你当前的代码中,Todos.getLayout是静态函数,不属于Todos组件的渲染上下文,无法直接访问组件内部的setQuery方法,直接这样写会触发错误。以下是两种可行的解决方式:

方案一:使用React Context传递回调

这种方式适合需要在多个组件间共享筛选状态的场景,结构清晰易维护。

  1. 创建筛选上下文
    新建TodoFilterContext.js文件:
import { createContext, useContext } from 'react';

const TodoFilterContext = createContext(null);

export const useTodoFilter = () => {
  const context = useContext(TodoFilterContext);
  if (!context) {
    throw new Error('useTodoFilter必须在TodoFilterProvider内部使用');
  }
  return context;
};

export const TodoFilterProvider = TodoFilterContext.Provider;
  1. 修改Todos页面组件
    在Todos组件中提供上下文,将setQuery传递给上下文:
import { useState } from 'react'
import Layout from './layout'
import { TodoFilterProvider } from './TodoFilterContext'

const Todos = () => {
  const [query, setQuery] = useState('')

  const todos = [
    {id: 0, text: 'make some projects'},
    {id: 1, text: 'fix some bugs'},
    {id: 2, text: 'cook food at home'}
  ]

  const searchedTodos = todos.filter(todo => todo.text.toLowerCase().includes(query.toLowerCase()))

  return (
    <TodoFilterProvider value={{ setQuery }}>
      <ul>
        {searchedTodos.map(todo => (
          <li key={todo.id}>{todo.text}</li>
        ))}
      </ul>
    </TodoFilterProvider>
  )
}

Todos.getLayout = function getLayout(page) {
  return <Layout>{page}</Layout>
}

export default Todos;
  1. 修改Layout组件
    在Layout中消费上下文,获取setQuery并绑定到输入框的onChange事件:
import { useTodoFilter } from './TodoFilterContext'

const Layout = ({children}) => {
  const { setQuery } = useTodoFilter()

  return (
    <div>
      <header>
        <div>Todo page</div>
        <input onChange={(e) => setQuery(e.target.value)} />
      </header>
      <main>{children}</main>
      <footer>
        some footer here
      </footer>
    </div>
  )
}

export default Layout

方案二:在getLayout中封装状态逻辑

这种方式适合简单场景,无需额外创建上下文文件。

修改todos.jsx,将状态逻辑移到getLayout的中间组件中,并将状态传递给页面和Layout:

import { useState, cloneElement } from 'react'
import Layout from './layout'

// 改造Todos组件,接收外部传入的query
const Todos = ({ query }) => {
  const todos = [
    {id: 0, text: 'make some projects'},
    {id: 1, text: 'fix some bugs'},
    {id: 2, text: 'cook food at home'}
  ]

  const searchedTodos = todos.filter(todo => todo.text.toLowerCase().includes(query.toLowerCase()))

  return (
    <ul>
      {searchedTodos.map(todo => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  )
}

Todos.getLayout = function getLayout(page) {
  // 创建带状态的中间组件,包裹Layout和页面
  const FilterWrapper = () => {
    const [query, setQuery] = useState('')
    return (
      <Layout onAppBarInputChange={(e) => setQuery(e.target.value)}>
        {/* 将query传递给页面组件 */}
        {cloneElement(page, { query })}
      </Layout>
    )
  }
  return <FilterWrapper />
}

export default Todos;

layout.jsx保持原代码即可:

const Layout = ({children, onAppBarInputChange}) => {
  return (
    <div>
      <header>
        <div>Todo page</div>
        <input onChange={onAppBarInputChange} />
      </header>
      <main>{children}</main>
      <footer>
        some footer here
      </footer>
    </div>
  )
}

export default Layout

内容的提问来源于stack exchange,提问作者Esa Kurniawan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 13:35:33