[英]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.