简体   繁体   English

如何使用 React/Next.js 根据元素 ID(使用 Netlify 表单)插入条件 HTML?

[英]How can I use React/Next.js to insert conditional HTML based on element ID (using Netlify forms)?

Apologies if this has been answered somewhere.抱歉,如果这已在某处得到回答。 I've searched and troubleshot for hours to no avail.我已经搜索和解决了几个小时无济于事。

First, I have yet to properly learn React, as it is new to me.首先,我还没有正确学习 React,因为它对我来说是新的。

I only want to use it for a contact form, and I have been following the tutorial over at FreeCodeCamp's website to do so.我只想将它用于联系表格,并且我一直在 FreeCodeCamp 的网站上按照教程进行操作。

The React version of the form displays fine, but does not submit messages nor does it show the success message.表单的 React 版本显示正常,但不提交消息,也不显示成功消息。

The HTML-only version of the form (part 1 of the aforementioned tutorial) submits messages fine, so I know I have the form itself set up correctly.表单的纯 HTML 版本(上述教程的第 1 部分)可以很好地提交消息,所以我知道我已经正确设置了表单本身。

I also tried building the same React app under Next.js (using npx create-next-app instead of npx create-react-app ) which somehow solves the problems (I don't know how), but introduces other problems.我还尝试在 Next.js 下构建相同的 React 应用程序(使用 npx npx create-next-app而不是 npx npx create-react-app ),它以某种方式解决了问题(我不知道如何),但引入了其他问题。

The HTML-only version isn't what I want (long story short), but either the React or Next.js versions will work fine, if I can fix the problems of either (or both), so I need help finding the easiest solution here please.纯 HTML 版本不是我想要的(长话短说),但 React 或 Next.js 版本都可以正常工作,如果我能解决其中一个(或两个)的问题,所以我需要帮助在这里找到最简单的解决方案请。

I could be wrong but I believe Netlify forms require the action element, so I'd like to avoid using onSubmit if at all possible.我可能是错的,但我相信 Netlify forms 需要action元素,所以我想尽可能避免使用onSubmit

Using the FreeCodeCamp code , the URL does not display ?success=true upon submitting the form, which I assume is the main culprit (or a symptom of it) behind the issues.使用FreeCodeCamp 代码,URL 在提交表单时不显示?success=true ,我认为这是问题背后的罪魁祸首(或它的症状)。

It uses this code in index.js to display form based on an ID in an existing HTML element (necessary because I'm building the app for a static site):它在index.js中使用此代码根据现有 HTML 元素中的 ID 显示表单(这是必要的,因为我正在为 static 站点构建应用程序):

import React from 'react';
import ReactDOM from 'react-dom/client';
import Contact from './contact-form';

const root = ReactDOM.createRoot(document.getElementById('contact-form'));
root.render(
  <React.StrictMode>
    <Contact />
  </React.StrictMode>
);

On the other hand, Next.js uses this code in its _app.js file (which works the same as React's index.js as far as I can tell):另一方面,Next.js 在其_app.js文件中使用此代码(据我所知,它与 React 的index.js相同):

import '../styles/main.css'

function Contact({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default Contact

The issue with Next.js is that it, by default, is not inserting the HTML into my other HTML file based on an ID. Next.js的问题在于,默认情况下,它不会基于 ID 将 HTML 插入我的其他 HTML 文件中。 In fact, it renders directly from the contact-form.js file (normally the App.js file but I renamed it).事实上,它直接从contact-form.js文件(通常是App.js文件,但我将其重命名)呈现。 I didn't think this would be hard to change, so I made _app.js look like this:我不认为这很难改变,所以我让_app.js看起来像这样:

import '../src/main.css' // for dev preview only
import React, { useEffect } from 'react';
import ReactDOM, { createRoot } from 'react-dom/client';
import Contact from './index';

const root = ReactDOM.createRoot(document.getElementById('contact-form'));
root.render(
  <React.StrictMode>
    <Contact />
  </React.StrictMode>
);

export default Contact

But this code fails because it cannot find "document" (I assume because it hasn't loaded yet).但是这段代码失败了,因为它找不到“文档”(我假设是因为它还没有加载)。 I looked around for solutions and couldn't understand how to implement useEffect but wrapping everything below import and above export with if (typeof window !== "undefined"){} did solve my issue...我环顾四周寻找解决方案,但无法理解如何实现useEffect但使用if (typeof window !== "undefined"){}将所有内容包装在import下方和export上方确实解决了我的问题...

And it introduced another one: Next.js now complains about an hydration failure.它还介绍了另一个:Next.js 现在抱怨水合失败。 After a bit of reading, I mostly understand what this means, but I still have no idea how to fix it.经过一番阅读,我基本明白这意味着什么,但我仍然不知道如何解决它。

I don't know whether the React version or Next.js version is easier to fix.我不知道是 React 版本还是 Next.js 版本更容易修复。 My preference is to get the React version fixed because I'll be focused on learning React in the near future, but the Next.js version seems easier to fix because it actually works, barring some annoying errors.我的偏好是修复 React 版本,因为我将在不久的将来专注于学习 React,但 Next.js 版本似乎更容易修复,因为它确实有效,除非出现一些恼人的错误。

I realise it's better to specify which version I need help with, but I don't know if one of them is fundamentally broken over the other and thus not worth trying to fix.我意识到最好指定我需要帮助的版本,但我不知道其中一个是否从根本上破坏了另一个,因此不值得尝试修复。

I'd really appreciate it if someone could give me a hand please.如果有人可以帮我一把,我将不胜感激。

For reference, though it's pretty much the same as in the aforementioned tutorial, here is my code that is being rendered:作为参考,虽然它与上述教程中的几乎相同,但这是我正在渲染的代码:

import './main.css'; // for dev preview only
import { useState, useEffect } from 'react';

function Contact() {
    const [success, setSuccess] = useState(false);
    
    useEffect(() => {
      if ( window.location.search.includes('success=true') ) {
        setSuccess(true);
      }
    }, []);

  return (
<div className="contact-form">
    <h2 className="grid-item grid-header">Contact Us</h2>
    <form className="grid-item grid-form" name="contact" id="contact-form" method="POST" action="/?success=true" data-netlify="true" data-netlify-recaptcha="true" netlify-honeypot="bot-field">
        <input type="hidden" name="contact" value="contact" />
        {success && (
          <p style={{ color: 'green'}}>
            Sent successfully
          </p>
        )}
        <p id="bot-field">
            <label className="grid-form-label">Do Not Fill</label>
            <input className="grid-form-item" type="text" name="bot-field" />
        </p>
        <p>
            <label className="grid-form-label" htmlFor="name">Name</label>
            <input className="grid-form-item" type="text" id="name" name="name" required />
        </p>
        <p>
            <label className="grid-form-label" htmlFor="email">Email</label>
            <input className="grid-form-item" type="text" id="email" name="email" required />
        </p>
        <p>
            <label className="grid-form-label" htmlFor="subject">Subject</label>
            <input className="grid-form-item" type="text" id="subject" name="subject" required />
        </p>
        <p>
            <label className="grid-form-label" htmlFor="message">Message</label>
            <textarea className="grid-form-text" id="message" name="message" required></textarea>
        </p>
        <div className="captcha" data-netlify-recaptcha="true"></div>
        <p>
            <button className="grid-form-btn" type="submit">Send</button>
        </p>
    </form>
</div>
  );
}

export default Contact;

The code in the other HTML file is simply:另一个 HTML 文件中的代码很简单:

<div id="contact-form-wrapper">
</div>

EDIT: I found a working solution, and have added it as an answer.编辑:我找到了一个可行的解决方案,并将其添加为答案。 Part of it was just an oversight on my part.其中一部分只是我的疏忽。 I'm still not completely sure why the original React-only version doesn't work but I will retry it just in case.我仍然不完全确定为什么最初的 React-only 版本不起作用,但我会重试以防万一。

I decided to try Netlify's version which moves the POST functionality into a script instead.我决定尝试Netlify 的版本,它将POST功能改为脚本。

After a lot of trial and error, I successfully integrated the success message.经过大量的试验和错误,我成功地集成了成功消息。 Here is my working code:这是我的工作代码:

import React from 'react';

const encode = (data) => {
return Object.keys(data)
    .map(key => encodeURIComponent(key) + "=" + encodeURIComponent(data[key]))
    .join("&");
}

class ContactForm extends React.Component {
constructor(props) {
  super(props);
  this.state = { name: "", email: "", subject: "", message: "", success: false };
}

handleSubmit = e => {
  fetch("/", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: encode({ "form-name": "contact", ...this.state })
  })
    .then(() => this.setState({ success: true }))
    .catch(error => this.setState({ success: false }));

  e.preventDefault();
};

handleChange = e => this.setState({ [e.target.name]: e.target.value });

render() {
    const { name, email, subject, message, success } = this.state;
    return (
            <div className="contact-form">
                <h2 className="grid-item grid-header">Contact Us</h2>
<form className="grid-item grid-form" name="contact" onSubmit={this.handleSubmit} data-netlify="true" netlify-honeypot="bot-field">
                    <input type="hidden" name="form-name" value="contact" />
                    {success && (
                      <p style={{ color: 'green'}}>
                        Sent successfully
                      </p>
                    )}
                    <p id="bot-field">
                        <label className="grid-form-label">Do Not Fill</label>
                        <input className="grid-form-item" type="text" name="bot-field" />
                    </p>
                    <p>
                        <label className="grid-form-label" htmlFor="name">Name</label>
                        <input className="grid-form-item" type="text" name="name" value={name} onChange={this.handleChange} required />
                    </p>
                    <p>
                        <label className="grid-form-label" htmlFor="email">Email</label>
                        <input className="grid-form-item" type="text" name="email" value={email} onChange={this.handleChange} required />
                    </p>
                    <p>
                        <label className="grid-form-label" htmlFor="subject">Subject</label>
                        <input className="grid-form-item" type="text" name="subject" value={subject} onChange={this.handleChange} required />
                    </p>
                    <p>
                        <label className="grid-form-label" htmlFor="message">Message</label>
                        <textarea className="grid-form-text" name="message" value={message} onChange={this.handleChange} required></textarea>
                    </p>
                    <p>
                        <button className="grid-form-btn" type="submit">Send</button>
                    </p>
                </form>
            </div>
        );
    }
}

export default ContactForm;

But there's something I glossed over in Netlify's documentation which makes this work, and that's the HTML part which is required.但是我在 Netlify 的文档中忽略了一些东西,这使得这项工作可以正常工作,那就是 HTML 部分是必需的。 A Netlify form requires you to mirror the JavaScript version in the HTML document where it'll be embedded, so it can detect the fields, which is why the HTML-only version worked and the others did not. Netlify 表单要求您在 HTML 文档中镜像 JavaScript 版本,以便它可以检测到字段,这就是为什么仅 HTML 版本有效而其他版本无效的原因。

This is the code in my contact.html file:这是我的contact.html文件中的代码:

<form name="contact" netlify netlify-honeypot="bot-field" hidden>
    <input type="text" name="bot-field" />
    <input type="text" name="name" />
    <input type="email" name="email" />
    <input type="text" name="subject" />
    <textarea name="message"></textarea>
</form>
<div id="contact-form-wrapper">
</div>

The only issue I haven't solved is that the form isn't cleared upon submission now, likely due to e.preventDefault();我没有解决的唯一问题是现在提交时表单没有被清除,可能是由于e.preventDefault(); but if I find a fix for that, I'll add it below.但如果我找到解决办法,我会在下面添加。

Also, the form expands upon submission to accommodate longer strings of text, but that's just a CSS issue as far as I can tell.此外,表单在提交时会扩展以容纳更长的文本字符串,但据我所知,这只是 CSS 问题。

EDIT: It turns out that Netlify's captcha only works on static HTML-only forms, so you need a custom captcha implementation, else the form breaks.编辑:事实证明,Netlify 的验证码仅适用于 static HTML-only forms,因此您需要自定义验证码实现,否则表单会中断。 I've edited the code to reflect that.我已经编辑了代码以反映这一点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM