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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:34:50