Next.js中如何向Layout组件传递Props实现待办项筛选
Next.js 单页面布局(Per-Page Layouts)向Layout传递回调的解决方案
你当前的代码中,Todos.getLayout是静态函数,不属于Todos组件的渲染上下文,无法直接访问组件内部的setQuery方法,直接这样写会触发错误。以下是两种可行的解决方式:
方案一:使用React Context传递回调
这种方式适合需要在多个组件间共享筛选状态的场景,结构清晰易维护。
- 创建筛选上下文
新建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;
- 修改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;
- 修改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
相关产品推荐
相关产品推荐

