GatsbyJS表单用户输入传递至GraphQL的实现示例求助
解决Gatsby中表单输入结合GraphQL查询的问题
我来帮你搞定这个表单输入和GraphQL查询结合的问题!你已经搞定了表单输入获取和GraphiQL的变量测试,接下来核心是根据你的数据存储方式(Drupal同步到Gatsby静态层,还是直接调用Drupal的API)选择合适的方案,下面分两种情况说明:
情况1:数据已通过gatsby-source-drupal同步到Gatsby静态层
如果你的食谱数据已经在构建时同步到Gatsby内部了,那Gatsby的静态GraphQL无法支持客户端动态传变量查询,这时候推荐用动态路由+页面查询的方式:
步骤1:创建动态结果页面
在src/pages/search/[ingredient].js创建页面组件,这个页面会接收URL中的ingredient作为变量,执行查询:
import { graphql } from 'gatsby'; export default function SearchResultsPage({ data, pageContext }) { const { ingredient } = pageContext; const recipes = data.allNodeRecipes.edges; return ( <div className="search-results"> <h1>包含「{ingredient}」的食谱</h1> {recipes.length === 0 ? ( <p>没有找到匹配的食谱哦</p> ) : ( <ul> {recipes.map(({ node }) => ( <li key={node.id}> <a href={`/${node.path.alias}`}>{node.title}</a> <p>食材:{node.relationships.field_ingredients.map(i => i.title).join(', ')}</p> </li> ))} </ul> )} </div> ); } // 页面查询,接收$ingredient变量 export const pageQuery = graphql` query SearchResultsQuery($ingredient: String) { allNodeRecipes(filter: { relationships: { field_ingredients: { elemMatch: { title: { eq: $ingredient } } } } }) { edges { node { id title path { alias } relationships { field_ingredients { title } } } } } } `;
步骤2:修改搜索表单,提交后跳转到动态路由
在你的搜索页面(比如src/pages/search.js)里,处理表单提交,跳转到带食材参数的动态页面:
import { useState } from 'react'; export default function SearchPage() { const [ingredient, setIngredient] = useState(''); const handleSubmit = (e) => { e.preventDefault(); if (ingredient.trim()) { // 跳转到动态路由,对食材名称编码避免特殊字符问题 window.location.href = `/search/${encodeURIComponent(ingredient)}`; } }; return ( <div className="search-form"> <form onSubmit={handleSubmit}> <input type="text" value={ingredient} onChange={(e) => setIngredient(e.target.value)} placeholder="输入食材(如鸡肉)" required /> <button type="submit">搜索食谱</button> </form> </div> ); }
步骤3:配置Gatsby的createPages(可选,预渲染已知食材页面)
如果你提前知道所有可能的食材,可以在gatsby-node.js里预渲染所有搜索结果页面,提升加载速度:
exports.createPages = async ({ graphql, actions }) => { const { createPage } = actions; // 获取所有食材 const result = await graphql(` query { allNodeRecipes { edges { node { relationships { field_ingredients { title } } } } } } `); // 去重食材列表 const ingredients = [...new Set( result.data.allNodeRecipes.edges.flatMap(edge => edge.node.relationships.field_ingredients.map(i => i.title) ) )]; // 为每个食材创建页面 ingredients.forEach(ingredient => { createPage({ path: `/search/${encodeURIComponent(ingredient)}`, component: require.resolve('./src/pages/search/[ingredient].js'), context: { ingredient, }, }); }); };
情况2:直接调用Drupal的GraphQL API(适合大数据量)
如果你的食谱数据量很大,不想全部同步到Gatsby静态层,那可以直接在客户端调用Drupal的GraphQL端点,用Apollo Client来动态查询:
步骤1:安装并配置Apollo Client
首先安装依赖:
npm install @apollo/client graphql gatsby-plugin-apollo
然后在gatsby-config.js里配置插件,指向你的Drupal GraphQL地址:
module.exports = { plugins: [ { resolve: 'gatsby-plugin-apollo', options: { uri: 'https://your-drupal-site.com/graphql', // 替换成你的Drupal GraphQL端点 }, }, ], };
步骤2:构建带动态查询的搜索页面
在搜索页面里用useLazyQuery来延迟执行查询,传递用户输入的食材变量:
import { useState } from 'react'; import { useLazyQuery, gql } from '@apollo/client'; // 定义查询语句(和你在GraphiQL里的一致) const SEARCH_RECIPES = gql` query SearchPageQuery($ingredient: String!) { nodeRecipes(filter: { relationships: { field_ingredients: { elemMatch: { title: { eq: $ingredient } } } } }) { edges { node { id title path { alias } relationships { field_ingredients { title } } } } } } `; export default function SearchPage() { const [ingredient, setIngredient] = useState(''); // useLazyQuery会返回触发查询的函数和查询状态 const [searchRecipes, { loading, data, error }] = useLazyQuery(SEARCH_RECIPES); const handleSubmit = (e) => { e.preventDefault(); if (ingredient.trim()) { // 传递变量到查询 searchRecipes({ variables: { ingredient } }); } }; return ( <div className="search-container"> <form onSubmit={handleSubmit}> <input type="text" value={ingredient} onChange={(e) => setIngredient(e.target.value)} placeholder="输入食材(如鸡肉)" required /> <button type="submit">搜索食谱</button> </form> {/* 处理查询状态 */} {loading && <p>正在加载食谱...</p>} {error && <p>搜索出错了:{error.message}</p>} {data && data.nodeRecipes.edges.length === 0 && <p>没有找到匹配的食谱</p>} {data && data.nodeRecipes.edges.length > 0 && ( <ul className="recipe-list"> {data.nodeRecipes.edges.map(({ node }) => ( <li key={node.id}> <a href={`/${node.path.alias}`}>{node.title}</a> <p>食材:{node.relationships.field_ingredients.map(i => i.title).join(', ')}</p> </li> ))} </ul> )} </div> ); }
关键注意事项
- 变量名要和查询中的
$ingredient完全一致,大小写敏感! - 如果用动态路由方案,要确保URL编码正确,避免特殊字符导致的路由问题。
- 如果直接调用Drupal API,要确保Drupal的GraphQL端点开启了对应的权限,允许匿名用户查询食谱数据。
- 两种方案各有优劣:动态路由方案利用Gatsby的静态生成优势,加载快;直接调用API方案适合大数据量,无需提前同步所有数据。
这样应该就能完美解决你把表单输入和GraphQL查询结合的问题啦!
内容的提问来源于stack exchange,提问作者ianckc
相关产品推荐
相关产品推荐

