![](/img/trans.png)
[英]Next.Js React app with styled components. Warning: Prop `className` did not match. Server: "x" Client: "y"
[英]Next.js, using random properties without triggering "did not match on server/client" error
我想隨機生成表單輸入的id
屬性,以防止它們與具有相同id
的其他輸入發生潛在沖突。 如果我在同一頁面上有兩個登錄表單,每個表單都有一個email
字段,則可能會發生這種情況。 我想要/需要設置id
屬性的原因是我可以在與該輸入對應的label
上設置for
屬性。 問題是這個隨機生成的id在服務端和客戶端是不一樣的,所以next.js會報錯。 這是一些代碼:
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>
)
}
這是我得到的錯誤:
警告:道具 'htmlFor' 不匹配。 服務器:“email-txdmls” 客戶端:“email-htte8e”
知道如何生成在服務器/客戶端上一致的隨機 id 嗎? 或者可能是沒有隨機ID的不同方式?
我找到了解決方法。 我不確定這是否是一個很好的解決方案(請參閱下面的解釋)。 只是為了抑制警告消息似乎很麻煩。 仍然很好奇聽到替代解決方案。 老實說,即使是告訴 next.js 忽略差異而不發出警告的方法也可以正常工作(SSR 和客戶端上的 id 不同並不重要)。
所以我所做的是在useEffect
鈎子中生成 id。 問題是初始服務器端呈現的 HTML 在輸入上沒有id
。 直到所有的 JS 都處理完才得到一個 id。 不理想。
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>
)
}
應該注意的是,第一次渲染時 id 將為null
。 在此示例中,這不是問題,因為目的主要是將標簽與輸入相關聯,這將在第二次渲染時足夠快地發生。 但是,如果您在其他情況下使用這個想法,請記住它。
如果您想將其封裝到自定義鈎子中,並稍微清理一下您的組件:
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>
)
}
我的解決方案是使用種子隨機數生成器而不是Math.random()
。 由於我在前端和后端都使用相同的種子,因此它們最終都會獲得相同的 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
}
當然,如果出於安全目的需要隨機數,則不應該這樣做。
getServerSideProps() 可以工作。
它可以在服務器中執行一些邏輯,然后將其傳遞給客戶端。 所以你可以讓一個隨機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.