简体   繁体   English

Next.js,使用随机属性而不触发“服务器/客户端不匹配”错误

[英]Next.js, using random properties without triggering "did not match on server/client" error

I'd like to randomly generate the id property of form inputs, to prevent them from potentially conflicting with other inputs with the same id .我想随机生成表单输入的id属性,以防止它们与具有相同id的其他输入发生潜在冲突。 This could happen if I have two login forms on the same page, each with an email field.如果我在同一页面上有两个登录表单,每个表单都有一个email字段,则可能会发生这种情况。 The reason I want/need to set the id property is so that I can set the for property on the label corresponding to that input.我想要/需要设置id属性的原因是我可以在与该输入对应的label上设置for属性。 The problem is that this randomly generated id is different on the server and the client, and so next.js throws an error.问题是这个随机生成的id在服务端和客户端是不一样的,所以next.js会报错。 Here's some code:这是一些代码:

function uniqueId() {
    let first = (Math.random() * 46656) | 0
    let second = (Math.random() * 46656) | 0
    first = ('000' + first.toString(36)).slice(-3)
    second = ('000' + second.toString(36)).slice(-3)
    return first + second
}

const Login = () => {
    const [emailId] = useState(uniqueId())

    return (
        <form>
            <label htmlFor={emailId}>Email</label>
            <input id={emailId} name='email' type='email' />
        </form>
    )
}

This is the error I get:这是我得到的错误:

Warning: Prop 'htmlFor' did not match.警告:道具 'htmlFor' 不匹配。 Server: "email-txdmls" Client: "email-htte8e"服务器:“email-txdmls” 客户端:“email-htte8e”

Any idea how to generate a random id that's consistent on the server/client?知道如何生成在服务器/客户端上一致的随机 id 吗? Or maybe a different way of doing it without random ids?或者可能是没有随机ID的不同方式?

I found a workaround to this.我找到了解决方法。 I'm not sure if it's a great solution (see explanation below).我不确定这是否是一个很好的解决方案(请参阅下面的解释)。 Seems like a lot of trouble just to essentially suppress a warning message.只是为了抑制警告消息似乎很麻烦。 Still very curious to hear alternate solutions.仍然很好奇听到替代解决方案。 Honestly even a way to tell next.js to ignore the difference and not issue a warning would work fine (it doesn't matter that the ids differ on SSR and client).老实说,即使是告诉 next.js 忽略差异而不发出警告的方法也可以正常工作(SSR 和客户端上的 id 不同并不重要)。

So what I did is generate the id in a useEffect hook.所以我所做的是在useEffect钩子中生成 id。 The problem is that initial server-side rendered HTML doesn't have an id on the input.问题是初始服务器端呈现的 HTML 在输入上没有id It's not until all the JS is processed that it gets an id.直到所有的 JS 都处理完才得到一个 id。 Not ideal.不理想。

const Login = () => {
    const [emailId, setEmailId] = useState(null)

    useEffect(() => {
        setEmailId(uniqueId())
    }, [])

    return (
        <form>
            <label htmlFor={emailId}>Email</label>
            <input id={emailId} name='email' type='email' />
        </form>
    )
}

It should be noted that the id will be null on the first render.应该注意的是,第一次渲染时 id 将为null In this example it isn't an issue since the purpose is mostly to associate a label with an input, which will happen quickly enough on the second render.在此示例中,这不是问题,因为目的主要是将标签与输入相关联,这将在第二次渲染时足够快地发生。 However, if you're using this idea in another situation, just keep it in mind.但是,如果您在其他情况下使用这个想法,请记住它。

If you want to encapsulate this into a custom hook, and clean up your component a bit:如果您想将其封装到自定义钩子中,并稍微清理一下您的组件:

const useUniqueId = () => {
    const [id, setId] = useState(null)

    useEffect(() => {
        setId(uniqueId())
    }, [])

    return id
}

const Login = () => {
    const emailId = useUniqueId()
    const nameId = useUniqueId()

    return (
        <form>
            <label htmlFor={nameId}>Name</label>
            <input id={nameId} name='name' type='text' />

            <label htmlFor={emailId}>Email</label>
            <input id={emailId} name='email' type='email' />
        </form>
    )
}

My solution was to use a seeded random number generator instead of Math.random() .我的解决方案是使用种子随机数生成器而不是Math.random() Since I use the same seed on both frontend and backend, they both end up getting the same ID-s.由于我在前端和后端都使用相同的种子,因此它们最终都会获得相同的 ID。

// https://stackoverflow.com/a/47593316/2405595
function createRandomSeedGenerator(str) {
  let h = 1779033703 ^ str.length;
  for (let i = 0; i < str.length; i++) {
    h = Math.imul(h ^ str.charCodeAt(i), 3432918353);
    h = (h << 13) | (h >>> 19);
  }

  return () => {
    h = Math.imul(h ^ (h >>> 16), 2246822507);
    h = Math.imul(h ^ (h >>> 13), 3266489909);
    return (h ^= h >>> 16) >>> 0;
  };
}

// https://stackoverflow.com/a/47593316/2405595
function createDeterministicRandom(seedString) {
  const generateSeed = createRandomSeedGenerator(seedString);
  let a = generateSeed();
  let b = generateSeed();
  let c = generateSeed();
  let d = generateSeed();

  return () => {
    a >>>= 0;
    b >>>= 0;
    c >>>= 0;
    d >>>= 0;
    var t = (a + b) | 0;
    a = b ^ (b >>> 9);
    b = (c + (c << 3)) | 0;
    c = (c << 21) | (c >>> 11);
    d = (d + 1) | 0;
    t = (t + d) | 0;
    c = (c + t) | 0;
    return (t >>> 0) / 4294967296;
  };
}


const deterministicRandomNumber = createDeterministicRandom(process.env.NODE_ENV);

function uniqueId() {
    let first = (deterministicRandomNumber() * 46656) | 0
    let second = (deterministicRandomNumber() * 46656) | 0
    first = ('000' + first.toString(36)).slice(-3)
    second = ('000' + second.toString(36)).slice(-3)
    return first + second
}

Of course, you should NOT do this if you need random numbers for security purposes.当然,如果出于安全目的需要随机数,则不应该这样做。

getServerSideProps() could work. getServerSideProps() 可以工作。

https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props#using-getserversideprops-to-fetch-data-at-request-time https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props#using-getserversideprops-to-fetch-data-at-request-time

It can do some logic in the server and then pass it to the client.它可以在服务器中执行一些逻辑,然后将其传递给客户端。 So you can make a random ID consistent on the server and client.所以你可以让一个随机ID在服务器和客户端上保持一致。

function Page({ randNum }) {
  return <div>{randNum}</div>;
}

// This gets called on every request
export async function getServerSideProps() {
  // Get random number
  randNum = Math.floor(Math.random() * 1000);

  // Pass data to the page via props
  return { props: { randNum } };
}

export default Page;

暂无
暂无

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

相关问题 Next.Js 带有样式组件的 React 应用程序。 警告:道具 `className` 不匹配。 服务器:“x” 客户端:“y” - Next.Js React app with styled components. Warning: Prop `className` did not match. Server: "x" Client: "y" 如何修复 `data-rbd-draggable-context-id` 不匹配。 服务器:“1”客户端:“0”,带有 react-beautiful-dnd 和 next.js - How to fix `data-rbd-draggable-context-id` did not match. Server: "1" Client: "0"' with react-beautiful-dnd and next.js 使用 Next.js 和自定义服务器通过 JSON 负载捕获错误客户端 - Catching error client side with JSON payload using Next.js and custom server 使用 next.js 构建的实时网站上的客户端获取错误 - client fetch error on live website built using next.js Next.js - 警告:道具 `dangerouslySetInnerHTML` 不匹配 - Next.js - Warning: Prop `dangerouslySetInnerHTML` did not match Next.js 错误:水化失败,因为初始 UI 与服务器上呈现的内容不匹配 - Next.js Error: Hydration failed because the initial UI does not match what was rendered on the server 用于服务器端渲染的 Next.js 路由器中的匹配和参数对象 - Match and Param object in Next.js router for Server side rendering 序列化错误 - 在 next.js 中使用 getServerSideProps - Error serializing - using getServerSideProps in next.js 错误 - 类型错误:使用 next.js 中的 redux 存储时无法读取未定义的属性(读取“getState”) - error - TypeError: Cannot read properties of undefined (reading 'getState') while using redux store in next.js Next.js 中的客户端和服务器端是什么? - What is client side and server side in Next.js?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM